neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

highlight_group.c (118677B)


      1 // highlight_group.c: code for managing highlight groups
      2 
      3 #include <assert.h>
      4 #include <ctype.h>
      5 #include <stdbool.h>
      6 #include <stdint.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include "klib/kvec.h"
     12 #include "nvim/api/keysets_defs.h"
     13 #include "nvim/api/private/defs.h"
     14 #include "nvim/api/private/dispatch.h"
     15 #include "nvim/api/private/helpers.h"
     16 #include "nvim/api/private/validate.h"
     17 #include "nvim/ascii_defs.h"
     18 #include "nvim/autocmd.h"
     19 #include "nvim/autocmd_defs.h"
     20 #include "nvim/buffer_defs.h"
     21 #include "nvim/charset.h"
     22 #include "nvim/cmdexpand_defs.h"
     23 #include "nvim/cursor_shape.h"
     24 #include "nvim/decoration_provider.h"
     25 #include "nvim/drawscreen.h"
     26 #include "nvim/errors.h"
     27 #include "nvim/eval.h"
     28 #include "nvim/eval/typval_defs.h"
     29 #include "nvim/eval/vars.h"
     30 #include "nvim/ex_docmd.h"
     31 #include "nvim/garray.h"
     32 #include "nvim/garray_defs.h"
     33 #include "nvim/gettext_defs.h"
     34 #include "nvim/globals.h"
     35 #include "nvim/grid_defs.h"
     36 #include "nvim/highlight.h"
     37 #include "nvim/highlight_group.h"
     38 #include "nvim/lua/executor.h"
     39 #include "nvim/macros_defs.h"
     40 #include "nvim/map_defs.h"
     41 #include "nvim/memory.h"
     42 #include "nvim/memory_defs.h"
     43 #include "nvim/message.h"
     44 #include "nvim/option.h"
     45 #include "nvim/option_defs.h"
     46 #include "nvim/option_vars.h"
     47 #include "nvim/os/time.h"
     48 #include "nvim/runtime.h"
     49 #include "nvim/strings.h"
     50 #include "nvim/types_defs.h"
     51 #include "nvim/ui.h"
     52 #include "nvim/ui_defs.h"
     53 #include "nvim/vim_defs.h"
     54 
     55 /// \addtogroup SG_SET
     56 /// @{
     57 enum {
     58  SG_CTERM = 2,  // cterm has been set
     59  SG_GUI = 4,    // gui has been set
     60  SG_LINK = 8,   // link has been set
     61 };
     62 /// @}
     63 
     64 #define MAX_SYN_NAME 200
     65 
     66 // builtin |highlight-groups|
     67 static garray_T highlight_ga = GA_EMPTY_INIT_VALUE;
     68 
     69 // arena for object with same lifetime as highlight_ga (aka hl_table)
     70 Arena highlight_arena = ARENA_EMPTY;
     71 
     72 Map(cstr_t, int) highlight_unames = MAP_INIT;
     73 
     74 /// The "term", "cterm" and "gui" arguments can be any combination of the
     75 /// following names, separated by commas (but no spaces!).
     76 static char *(hl_name_table[]) =
     77 { "bold", "standout", "underline",
     78  "undercurl", "underdouble", "underdotted", "underdashed",
     79  "italic", "reverse", "inverse", "strikethrough", "altfont",
     80  "dim", "blink", "conceal", "overline", "nocombine", "NONE" };
     81 static int hl_attr_table[] =
     82 { HL_BOLD, HL_STANDOUT, HL_UNDERLINE,
     83  HL_UNDERCURL, HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED,
     84  HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_ALTFONT,
     85  HL_DIM, HL_BLINK, HL_CONCEALED, HL_OVERLINE, HL_NOCOMBINE, 0 };
     86 
     87 /// Structure that stores information about a highlight group.
     88 /// The ID of a highlight group is also called group ID.  It is the index in
     89 /// the highlight_ga array PLUS ONE.
     90 typedef struct {
     91  char *sg_name;                ///< highlight group name
     92  char *sg_name_u;              ///< uppercase of sg_name
     93  bool sg_cleared;              ///< "hi clear" was used
     94  int sg_attr;                  ///< Screen attr @see ATTR_ENTRY
     95  int sg_link;                  ///< link to this highlight group ID
     96  int sg_deflink;               ///< default link; restored in highlight_clear()
     97  int sg_set;                   ///< combination of flags in \ref SG_SET
     98  sctx_T sg_deflink_sctx;       ///< script where the default link was set
     99  sctx_T sg_script_ctx;         ///< script in which the group was last set for terminal UIs
    100  int sg_cterm;                 ///< "cterm=" highlighting attr
    101                                ///< (combination of \ref HlAttrFlags)
    102  int sg_cterm_fg;              ///< terminal fg color number + 1
    103  int sg_cterm_bg;              ///< terminal bg color number + 1
    104  bool sg_cterm_bold;           ///< bold attr was set for light color for RGB UIs
    105  int sg_gui;                   ///< "gui=" highlighting attributes
    106                                ///< (combination of \ref HlAttrFlags)
    107  RgbValue sg_rgb_fg;           ///< RGB foreground color
    108  RgbValue sg_rgb_bg;           ///< RGB background color
    109  RgbValue sg_rgb_sp;           ///< RGB special color
    110  int sg_rgb_fg_idx;            ///< RGB foreground color index
    111  int sg_rgb_bg_idx;            ///< RGB background color index
    112  int sg_rgb_sp_idx;            ///< RGB special color index
    113 
    114  int sg_blend;                 ///< blend level (0-100 inclusive), -1 if unset
    115 
    116  int sg_parent;                ///< parent of @nested.group
    117 } HlGroup;
    118 
    119 enum {
    120  kColorIdxNone = -1,
    121  kColorIdxHex = -2,
    122  kColorIdxFg = -3,
    123  kColorIdxBg = -4,
    124 };
    125 
    126 #include "highlight_group.c.generated.h"
    127 
    128 static const char e_highlight_group_name_not_found_str[]
    129  = N_("E411: Highlight group not found: %s");
    130 static const char e_group_has_settings_highlight_link_ignored[]
    131  = N_("E414: Group has settings, highlight link ignored");
    132 static const char e_unexpected_equal_sign_str[]
    133  = N_("E415: Unexpected equal sign: %s");
    134 static const char e_missing_equal_sign_str_2[]
    135  = N_("E416: Missing equal sign: %s");
    136 static const char e_missing_argument_str[]
    137  = N_("E417: Missing argument: %s");
    138 
    139 #define hl_table ((HlGroup *)((highlight_ga.ga_data)))
    140 
    141 // The default highlight groups.  These are compiled-in for fast startup and
    142 // they still work when the runtime files can't be found.
    143 
    144 static const char *highlight_init_both[] = {
    145  "Cursor            guifg=bg      guibg=fg",
    146  "CursorLineNr      gui=bold      cterm=bold",
    147  "PmenuMatch        gui=bold      cterm=bold",
    148  "PmenuMatchSel     gui=bold      cterm=bold",
    149  "PmenuSel          gui=reverse   cterm=reverse,underline blend=0",
    150  "RedrawDebugNormal gui=reverse   cterm=reverse",
    151  "TabLineSel        gui=bold      cterm=NONE",
    152  "TermCursor        gui=reverse   cterm=reverse",
    153  "Underlined        gui=underline cterm=underline",
    154  "lCursor           guifg=bg      guibg=fg",
    155 
    156  // UI
    157  "default link CursorIM         Cursor",
    158  "default link CursorLineFold   FoldColumn",
    159  "default link CursorLineSign   SignColumn",
    160  "default link DiffTextAdd      DiffText",
    161  "default link EndOfBuffer      NonText",
    162  "default link FloatBorder      NormalFloat",
    163  "default link FloatFooter      FloatTitle",
    164  "default link FloatTitle       Title",
    165  "default link FoldColumn       SignColumn",
    166  "default link IncSearch        CurSearch",
    167  "default link LineNrAbove      LineNr",
    168  "default link LineNrBelow      LineNr",
    169  "default link MsgSeparator     StatusLine",
    170  "default link MsgArea          NONE",
    171  "default link NormalNC         NONE",
    172  "default link PmenuExtra       Pmenu",
    173  "default link PmenuExtraSel    PmenuSel",
    174  "default link PmenuKind        Pmenu",
    175  "default link PmenuKindSel     PmenuSel",
    176  "default link PmenuSbar        Pmenu",
    177  "default link PmenuBorder        Pmenu",
    178  "default link PmenuShadow        FloatShadow",
    179  "default link PmenuShadowThrough FloatShadowThrough",
    180  "default link PreInsert        Added",
    181  "default link ComplMatchIns    NONE",
    182  "default link ComplHint        NonText",
    183  "default link ComplHintMore    MoreMsg",
    184  "default link Substitute       Search",
    185  "default link StatusLineTerm   StatusLine",
    186  "default link StatusLineTermNC StatusLineNC",
    187  "default link StderrMsg        ErrorMsg",
    188  "default link StdoutMsg        NONE",
    189  "default link TabLine          StatusLineNC",
    190  "default link TabLineFill      TabLine",
    191  "default link VertSplit        WinSeparator",
    192  "default link VisualNOS        Visual",
    193  "default link Whitespace       NonText",
    194  "default link WildMenu         PmenuSel",
    195  "default link WinSeparator     Normal",
    196 
    197  // Syntax
    198  "default link Character      Constant",
    199  "default link Number         Constant",
    200  "default link Boolean        Constant",
    201  "default link Float          Number",
    202  "default link Conditional    Statement",
    203  "default link Repeat         Statement",
    204  "default link Label          Statement",
    205  "default link Keyword        Statement",
    206  "default link Exception      Statement",
    207  "default link Include        PreProc",
    208  "default link Define         PreProc",
    209  "default link Macro          PreProc",
    210  "default link PreCondit      PreProc",
    211  "default link StorageClass   Type",
    212  "default link Structure      Type",
    213  "default link Typedef        Type",
    214  "default link Tag            Special",
    215  "default link SpecialChar    Special",
    216  "default link SpecialComment Special",
    217  "default link Debug          Special",
    218  // Used by HLF_8 (very common). None of the HLF_* things use the other Special* groups.
    219  "default link SpecialKey     Special",
    220  "default link Ignore         Normal",
    221 
    222  // Built-in LSP
    223  "default link LspCodeLens                 NonText",
    224  "default link LspCodeLensSeparator        LspCodeLens",
    225  "default link LspInlayHint                NonText",
    226  "default link LspReferenceRead            LspReferenceText",
    227  "default link LspReferenceText            Visual",
    228  "default link LspReferenceWrite           LspReferenceText",
    229  "default link LspReferenceTarget          LspReferenceText",
    230  "default link LspSignatureActiveParameter Visual",
    231  "default link SnippetTabstop              Visual",
    232  "default link SnippetTabstopActive        SnippetTabstop",
    233 
    234  // Diagnostic
    235  "default link DiagnosticFloatingError    DiagnosticError",
    236  "default link DiagnosticFloatingWarn     DiagnosticWarn",
    237  "default link DiagnosticFloatingInfo     DiagnosticInfo",
    238  "default link DiagnosticFloatingHint     DiagnosticHint",
    239  "default link DiagnosticFloatingOk       DiagnosticOk",
    240  "default link DiagnosticVirtualTextError DiagnosticError",
    241  "default link DiagnosticVirtualTextWarn  DiagnosticWarn",
    242  "default link DiagnosticVirtualTextInfo  DiagnosticInfo",
    243  "default link DiagnosticVirtualTextHint  DiagnosticHint",
    244  "default link DiagnosticVirtualTextOk    DiagnosticOk",
    245  "default link DiagnosticVirtualLinesError DiagnosticError",
    246  "default link DiagnosticVirtualLinesWarn  DiagnosticWarn",
    247  "default link DiagnosticVirtualLinesInfo  DiagnosticInfo",
    248  "default link DiagnosticVirtualLinesHint  DiagnosticHint",
    249  "default link DiagnosticVirtualLinesOk    DiagnosticOk",
    250  "default link DiagnosticSignError        DiagnosticError",
    251  "default link DiagnosticSignWarn         DiagnosticWarn",
    252  "default link DiagnosticSignInfo         DiagnosticInfo",
    253  "default link DiagnosticSignHint         DiagnosticHint",
    254  "default link DiagnosticSignOk           DiagnosticOk",
    255  "default link DiagnosticUnnecessary      Comment",
    256 
    257  // Treesitter standard groups
    258  "default link @variable.builtin           Special",
    259  "default link @variable.parameter.builtin Special",
    260 
    261  "default link @constant         Constant",
    262  "default link @constant.builtin Special",
    263 
    264  "default link @module         Structure",
    265  "default link @module.builtin Special",
    266  "default link @label          Label",
    267 
    268  "default link @string             String",
    269  "default link @string.regexp      @string.special",
    270  "default link @string.escape      @string.special",
    271  "default link @string.special     SpecialChar",
    272  "default link @string.special.url Underlined",
    273 
    274  "default link @character         Character",
    275  "default link @character.special SpecialChar",
    276 
    277  "default link @boolean      Boolean",
    278  "default link @number       Number",
    279  "default link @number.float Float",
    280 
    281  "default link @type         Type",
    282  "default link @type.builtin Special",
    283 
    284  "default link @attribute         Macro",
    285  "default link @attribute.builtin Special",
    286  "default link @property          Identifier",
    287 
    288  "default link @function         Function",
    289  "default link @function.builtin Special",
    290 
    291  "default link @constructor Special",
    292  "default link @operator    Operator",
    293 
    294  "default link @keyword Keyword",
    295 
    296  "default link @punctuation         Delimiter",  // fallback for subgroups; never used itself
    297  "default link @punctuation.special Special",
    298 
    299  "default link @comment Comment",
    300 
    301  "default link @comment.error   DiagnosticError",
    302  "default link @comment.warning DiagnosticWarn",
    303  "default link @comment.note    DiagnosticInfo",
    304  "default link @comment.todo    Todo",
    305 
    306  "@markup.strong        gui=bold          cterm=bold",
    307  "@markup.italic        gui=italic        cterm=italic",
    308  "@markup.strikethrough gui=strikethrough cterm=strikethrough",
    309  "@markup.underline     gui=underline     cterm=underline",
    310 
    311  "default link @markup         Special",  // fallback for subgroups; never used itself
    312  "default link @markup.heading Title",
    313  "default link @markup.link    Underlined",
    314 
    315  "default link @diff.plus  Added",
    316  "default link @diff.minus Removed",
    317  "default link @diff.delta Changed",
    318 
    319  "default link @tag         Tag",
    320  "default link @tag.builtin Special",
    321 
    322  // :help
    323  // Highlight "===" and "---" heading delimiters specially.
    324  "default @markup.heading.1.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underdouble,nocombine ctermbg=NONE ctermfg=NONE cterm=underdouble,nocombine",
    325  "default @markup.heading.2.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underline,nocombine ctermbg=NONE ctermfg=NONE cterm=underline,nocombine",
    326 
    327  // LSP semantic tokens
    328  "default link @lsp.type.class         @type",
    329  "default link @lsp.type.comment       @comment",
    330  "default link @lsp.type.decorator     @attribute",
    331  "default link @lsp.type.enum          @type",
    332  "default link @lsp.type.enumMember    @constant",
    333  "default link @lsp.type.event         @type",
    334  "default link @lsp.type.function      @function",
    335  "default link @lsp.type.interface     @type",
    336  "default link @lsp.type.keyword       @keyword",
    337  "default link @lsp.type.macro         @constant.macro",
    338  "default link @lsp.type.method        @function.method",
    339  "default link @lsp.type.modifier      @type.qualifier",
    340  "default link @lsp.type.namespace     @module",
    341  "default link @lsp.type.number        @number",
    342  "default link @lsp.type.operator      @operator",
    343  "default link @lsp.type.parameter     @variable.parameter",
    344  "default link @lsp.type.property      @property",
    345  "default link @lsp.type.regexp        @string.regexp",
    346  "default link @lsp.type.string        @string",
    347  "default link @lsp.type.struct        @type",
    348  "default link @lsp.type.type          @type",
    349  "default link @lsp.type.typeParameter @type.definition",
    350  "default link @lsp.type.variable      @variable",
    351 
    352  "default link @lsp.mod.deprecated DiagnosticDeprecated",
    353 
    354  NULL
    355 };
    356 
    357 // Default colors only used with a light background.
    358 static const char *highlight_init_light[] = {
    359  "Normal guifg=NvimDarkGrey2 guibg=NvimLightGrey2 ctermfg=NONE ctermbg=NONE",
    360 
    361  // UI
    362  "Added                guifg=NvimDarkGreen                                  ctermfg=2",
    363  "Changed              guifg=NvimDarkCyan                                   ctermfg=6",
    364  "ColorColumn                               guibg=NvimLightGrey4            cterm=reverse",
    365  "Conceal              guifg=NvimLightGrey4",
    366  "CurSearch            guifg=NvimLightGrey1 guibg=NvimDarkYellow            ctermfg=15 ctermbg=3",
    367  "CursorColumn                              guibg=NvimLightGrey3",
    368  "CursorLine                                guibg=NvimLightGrey3",
    369  "DiffAdd              guifg=NvimDarkGrey1  guibg=NvimLightGreen            ctermfg=15 ctermbg=2",
    370  "DiffChange           guifg=NvimDarkGrey1  guibg=NvimLightGrey4",
    371  "DiffDelete           guifg=NvimDarkRed                          gui=bold  ctermfg=1 cterm=bold",
    372  "DiffText             guifg=NvimDarkGrey1  guibg=NvimLightCyan             ctermfg=15 ctermbg=6",
    373  "Directory            guifg=NvimDarkCyan                                   ctermfg=6",
    374  "ErrorMsg             guifg=NvimDarkRed                                    ctermfg=1",
    375  "FloatShadow                               guibg=NvimLightGrey4            ctermbg=0 blend=80",
    376  "FloatShadowThrough                        guibg=NvimLightGrey4            ctermbg=0 blend=100",
    377  "Folded               guifg=NvimDarkGrey4  guibg=NvimLightGrey1",
    378  "LineNr               guifg=NvimLightGrey4",
    379  "MatchParen                                guibg=NvimLightGrey4  gui=bold  cterm=bold,underline",
    380  "ModeMsg              guifg=NvimDarkGreen                                  ctermfg=2",
    381  "MoreMsg              guifg=NvimDarkCyan                                   ctermfg=6",
    382  "NonText              guifg=NvimLightGrey4",
    383  "NormalFloat                               guibg=NvimLightGrey1",
    384  "OkMsg                guifg=NvimDarkGreen                                  ctermfg=2",
    385  "Pmenu                                     guibg=NvimLightGrey3            cterm=reverse",
    386  "PmenuThumb                                guibg=NvimLightGrey4",
    387  "Question             guifg=NvimDarkCyan                                   ctermfg=6",
    388  "QuickFixLine         guifg=NvimDarkCyan                                   ctermfg=6",
    389  "RedrawDebugClear                          guibg=NvimLightYellow           ctermfg=15 ctermbg=3",
    390  "RedrawDebugComposed                       guibg=NvimLightGreen            ctermfg=15 ctermbg=2",
    391  "RedrawDebugRecompose                      guibg=NvimLightRed              ctermfg=15 ctermbg=1",
    392  "Removed              guifg=NvimDarkRed                                    ctermfg=1",
    393  "Search               guifg=NvimDarkGrey1  guibg=NvimLightYellow           ctermfg=15 ctermbg=3",
    394  "SignColumn           guifg=NvimLightGrey4",
    395  "SpellBad             guisp=NvimDarkRed    gui=undercurl                   cterm=undercurl",
    396  "SpellCap             guisp=NvimDarkYellow gui=undercurl                   cterm=undercurl",
    397  "SpellLocal           guisp=NvimDarkGreen  gui=undercurl                   cterm=undercurl",
    398  "SpellRare            guisp=NvimDarkCyan   gui=undercurl                   cterm=undercurl",
    399  "StatusLine           guifg=NvimDarkGrey2  guibg=NvimLightGrey4            cterm=reverse",
    400  "StatusLineNC         guifg=NvimDarkGrey3  guibg=NvimLightGrey3            cterm=bold,underline",
    401  "Title                guifg=NvimDarkGrey2                        gui=bold  cterm=bold",
    402  "Visual                                    guibg=NvimLightGrey4            ctermfg=15 ctermbg=0",
    403  "WarningMsg           guifg=NvimDarkYellow                                 ctermfg=3",
    404  "WinBar               guifg=NvimDarkGrey4  guibg=NvimLightGrey1  gui=bold  cterm=bold",
    405  "WinBarNC             guifg=NvimDarkGrey4  guibg=NvimLightGrey1            cterm=bold",
    406 
    407  // Syntax
    408  "Constant   guifg=NvimDarkGrey2",  // Use only `Normal` foreground to be usable on different background
    409  "Operator   guifg=NvimDarkGrey2",
    410  "PreProc    guifg=NvimDarkGrey2",
    411  "Type       guifg=NvimDarkGrey2",
    412  "Delimiter  guifg=NvimDarkGrey2",
    413 
    414  "Comment    guifg=NvimDarkGrey4",
    415  "String     guifg=NvimDarkGreen                    ctermfg=2",
    416  "Identifier guifg=NvimDarkBlue                     ctermfg=4",
    417  "Function   guifg=NvimDarkCyan                     ctermfg=6",
    418  "Statement  guifg=NvimDarkGrey2 gui=bold           cterm=bold",
    419  "Special    guifg=NvimDarkCyan                     ctermfg=6",
    420  "Error      guifg=NvimDarkGrey1 guibg=NvimLightRed ctermfg=15 ctermbg=1",
    421  "Todo       guifg=NvimDarkGrey2 gui=bold           cterm=bold",
    422 
    423  // Diagnostic
    424  "DiagnosticError          guifg=NvimDarkRed                      ctermfg=1",
    425  "DiagnosticWarn           guifg=NvimDarkYellow                   ctermfg=3",
    426  "DiagnosticInfo           guifg=NvimDarkCyan                     ctermfg=6",
    427  "DiagnosticHint           guifg=NvimDarkBlue                     ctermfg=4",
    428  "DiagnosticOk             guifg=NvimDarkGreen                    ctermfg=2",
    429  "DiagnosticUnderlineError guisp=NvimDarkRed    gui=underline     cterm=underline",
    430  "DiagnosticUnderlineWarn  guisp=NvimDarkYellow gui=underline     cterm=underline",
    431  "DiagnosticUnderlineInfo  guisp=NvimDarkCyan   gui=underline     cterm=underline",
    432  "DiagnosticUnderlineHint  guisp=NvimDarkBlue   gui=underline     cterm=underline",
    433  "DiagnosticUnderlineOk    guisp=NvimDarkGreen  gui=underline     cterm=underline",
    434  "DiagnosticDeprecated     guisp=NvimDarkRed    gui=strikethrough cterm=strikethrough",
    435 
    436  // Treesitter standard groups
    437  "@variable guifg=NvimDarkGrey2",
    438  NULL
    439 };
    440 
    441 // Default colors only used with a dark background.
    442 static const char *highlight_init_dark[] = {
    443  "Normal guifg=NvimLightGrey2 guibg=NvimDarkGrey2 ctermfg=NONE ctermbg=NONE",
    444 
    445  // UI
    446  "Added                guifg=NvimLightGreen                                ctermfg=10",
    447  "Changed              guifg=NvimLightCyan                                 ctermfg=14",
    448  "ColorColumn                                guibg=NvimDarkGrey4           cterm=reverse",
    449  "Conceal              guifg=NvimDarkGrey4",
    450  "CurSearch            guifg=NvimDarkGrey1   guibg=NvimLightYellow         ctermfg=0 ctermbg=11",
    451  "CursorColumn                               guibg=NvimDarkGrey3",
    452  "CursorLine                                 guibg=NvimDarkGrey3",
    453  "DiffAdd              guifg=NvimLightGrey1  guibg=NvimDarkGreen           ctermfg=0 ctermbg=10",
    454  "DiffChange           guifg=NvimLightGrey1  guibg=NvimDarkGrey4",
    455  "DiffDelete           guifg=NvimLightRed                         gui=bold ctermfg=9 cterm=bold",
    456  "DiffText             guifg=NvimLightGrey1  guibg=NvimDarkCyan            ctermfg=0 ctermbg=14",
    457  "Directory            guifg=NvimLightCyan                                 ctermfg=14",
    458  "ErrorMsg             guifg=NvimLightRed                                  ctermfg=9",
    459  "FloatShadow                                guibg=NvimDarkGrey4           ctermbg=0 blend=80",
    460  "FloatShadowThrough                         guibg=NvimDarkGrey4           ctermbg=0 blend=100",
    461  "Folded               guifg=NvimLightGrey4  guibg=NvimDarkGrey1",
    462  "LineNr               guifg=NvimDarkGrey4",
    463  "MatchParen                                 guibg=NvimDarkGrey4  gui=bold cterm=bold,underline",
    464  "ModeMsg              guifg=NvimLightGreen                                ctermfg=10",
    465  "MoreMsg              guifg=NvimLightCyan                                 ctermfg=14",
    466  "NonText              guifg=NvimDarkGrey4",
    467  "NormalFloat                                guibg=NvimDarkGrey1",
    468  "OkMsg                guifg=NvimLightGreen                                ctermfg=10",
    469  "Pmenu                                      guibg=NvimDarkGrey3           cterm=reverse",
    470  "PmenuThumb                                 guibg=NvimDarkGrey4",
    471  "Question             guifg=NvimLightCyan                                 ctermfg=14",
    472  "QuickFixLine         guifg=NvimLightCyan                                 ctermfg=14",
    473  "RedrawDebugClear                           guibg=NvimDarkYellow          ctermfg=0 ctermbg=11",
    474  "RedrawDebugComposed                        guibg=NvimDarkGreen           ctermfg=0 ctermbg=10",
    475  "RedrawDebugRecompose                       guibg=NvimDarkRed             ctermfg=0 ctermbg=9",
    476  "Removed              guifg=NvimLightRed                                  ctermfg=9",
    477  "Search               guifg=NvimLightGrey1  guibg=NvimDarkYellow          ctermfg=0 ctermbg=11",
    478  "SignColumn           guifg=NvimDarkGrey4",
    479  "SpellBad             guisp=NvimLightRed    gui=undercurl                 cterm=undercurl",
    480  "SpellCap             guisp=NvimLightYellow gui=undercurl                 cterm=undercurl",
    481  "SpellLocal           guisp=NvimLightGreen  gui=undercurl                 cterm=undercurl",
    482  "SpellRare            guisp=NvimLightCyan   gui=undercurl                 cterm=undercurl",
    483  "StatusLine           guifg=NvimLightGrey2  guibg=NvimDarkGrey4           cterm=reverse",
    484  "StatusLineNC         guifg=NvimLightGrey3  guibg=NvimDarkGrey3           cterm=bold,underline",
    485  "Title                guifg=NvimLightGrey2                       gui=bold cterm=bold",
    486  "Visual                                     guibg=NvimDarkGrey4           ctermfg=0 ctermbg=15",
    487  "WarningMsg           guifg=NvimLightYellow                               ctermfg=11",
    488  "WinBar               guifg=NvimLightGrey4  guibg=NvimDarkGrey1  gui=bold cterm=bold",
    489  "WinBarNC             guifg=NvimLightGrey4  guibg=NvimDarkGrey1           cterm=bold",
    490 
    491  // Syntax
    492  "Constant   guifg=NvimLightGrey2",  // Use only `Normal` foreground to be usable on different background
    493  "Operator   guifg=NvimLightGrey2",
    494  "PreProc    guifg=NvimLightGrey2",
    495  "Type       guifg=NvimLightGrey2",
    496  "Delimiter  guifg=NvimLightGrey2",
    497 
    498  "Comment    guifg=NvimLightGrey4",
    499  "String     guifg=NvimLightGreen                   ctermfg=10",
    500  "Identifier guifg=NvimLightBlue                    ctermfg=12",
    501  "Function   guifg=NvimLightCyan                    ctermfg=14",
    502  "Statement  guifg=NvimLightGrey2 gui=bold          cterm=bold",
    503  "Special    guifg=NvimLightCyan                    ctermfg=14",
    504  "Error      guifg=NvimLightGrey1 guibg=NvimDarkRed ctermfg=0 ctermbg=9",
    505  "Todo       guifg=NvimLightGrey2 gui=bold          cterm=bold",
    506 
    507  // Diagnostic
    508  "DiagnosticError          guifg=NvimLightRed                      ctermfg=9",
    509  "DiagnosticWarn           guifg=NvimLightYellow                   ctermfg=11",
    510  "DiagnosticInfo           guifg=NvimLightCyan                     ctermfg=14",
    511  "DiagnosticHint           guifg=NvimLightBlue                     ctermfg=12",
    512  "DiagnosticOk             guifg=NvimLightGreen                    ctermfg=10",
    513  "DiagnosticUnderlineError guisp=NvimLightRed    gui=underline     cterm=underline",
    514  "DiagnosticUnderlineWarn  guisp=NvimLightYellow gui=underline     cterm=underline",
    515  "DiagnosticUnderlineInfo  guisp=NvimLightCyan   gui=underline     cterm=underline",
    516  "DiagnosticUnderlineHint  guisp=NvimLightBlue   gui=underline     cterm=underline",
    517  "DiagnosticUnderlineOk    guisp=NvimLightGreen  gui=underline     cterm=underline",
    518  "DiagnosticDeprecated     guisp=NvimLightRed    gui=strikethrough cterm=strikethrough",
    519 
    520  // Treesitter standard groups
    521  "@variable guifg=NvimLightGrey2",
    522  NULL
    523 };
    524 
    525 const char *const highlight_init_cmdline[] = {
    526  // XXX When modifying a list modify it in both valid and invalid halves.
    527  // TODO(ZyX-I): merge valid and invalid groups via a macros.
    528 
    529  // NvimInternalError should appear only when highlighter has a bug.
    530  "NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red",
    531 
    532  // Highlight groups (links) used by parser:
    533 
    534  "default link NvimAssignment Operator",
    535  "default link NvimPlainAssignment NvimAssignment",
    536  "default link NvimAugmentedAssignment NvimAssignment",
    537  "default link NvimAssignmentWithAddition NvimAugmentedAssignment",
    538  "default link NvimAssignmentWithSubtraction NvimAugmentedAssignment",
    539  "default link NvimAssignmentWithConcatenation NvimAugmentedAssignment",
    540 
    541  "default link NvimOperator Operator",
    542 
    543  "default link NvimUnaryOperator NvimOperator",
    544  "default link NvimUnaryPlus NvimUnaryOperator",
    545  "default link NvimUnaryMinus NvimUnaryOperator",
    546  "default link NvimNot NvimUnaryOperator",
    547 
    548  "default link NvimBinaryOperator NvimOperator",
    549  "default link NvimComparison NvimBinaryOperator",
    550  "default link NvimComparisonModifier NvimComparison",
    551  "default link NvimBinaryPlus NvimBinaryOperator",
    552  "default link NvimBinaryMinus NvimBinaryOperator",
    553  "default link NvimConcat NvimBinaryOperator",
    554  "default link NvimConcatOrSubscript NvimConcat",
    555  "default link NvimOr NvimBinaryOperator",
    556  "default link NvimAnd NvimBinaryOperator",
    557  "default link NvimMultiplication NvimBinaryOperator",
    558  "default link NvimDivision NvimBinaryOperator",
    559  "default link NvimMod NvimBinaryOperator",
    560 
    561  "default link NvimTernary NvimOperator",
    562  "default link NvimTernaryColon NvimTernary",
    563 
    564  "default link NvimParenthesis Delimiter",
    565  "default link NvimLambda NvimParenthesis",
    566  "default link NvimNestingParenthesis NvimParenthesis",
    567  "default link NvimCallingParenthesis NvimParenthesis",
    568 
    569  "default link NvimSubscript NvimParenthesis",
    570  "default link NvimSubscriptBracket NvimSubscript",
    571  "default link NvimSubscriptColon NvimSubscript",
    572  "default link NvimCurly NvimSubscript",
    573 
    574  "default link NvimContainer NvimParenthesis",
    575  "default link NvimDict NvimContainer",
    576  "default link NvimList NvimContainer",
    577 
    578  "default link NvimIdentifier Identifier",
    579  "default link NvimIdentifierScope NvimIdentifier",
    580  "default link NvimIdentifierScopeDelimiter NvimIdentifier",
    581  "default link NvimIdentifierName NvimIdentifier",
    582  "default link NvimIdentifierKey NvimIdentifier",
    583 
    584  "default link NvimColon Delimiter",
    585  "default link NvimComma Delimiter",
    586  "default link NvimArrow Delimiter",
    587 
    588  "default link NvimRegister SpecialChar",
    589  "default link NvimNumber Number",
    590  "default link NvimFloat NvimNumber",
    591  "default link NvimNumberPrefix Type",
    592 
    593  "default link NvimOptionSigil Type",
    594  "default link NvimOptionName NvimIdentifier",
    595  "default link NvimOptionScope NvimIdentifierScope",
    596  "default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter",
    597 
    598  "default link NvimEnvironmentSigil NvimOptionSigil",
    599  "default link NvimEnvironmentName NvimIdentifier",
    600 
    601  "default link NvimString String",
    602  "default link NvimStringBody NvimString",
    603  "default link NvimStringQuote NvimString",
    604  "default link NvimStringSpecial SpecialChar",
    605 
    606  "default link NvimSingleQuote NvimStringQuote",
    607  "default link NvimSingleQuotedBody NvimStringBody",
    608  "default link NvimSingleQuotedQuote NvimStringSpecial",
    609 
    610  "default link NvimDoubleQuote NvimStringQuote",
    611  "default link NvimDoubleQuotedBody NvimStringBody",
    612  "default link NvimDoubleQuotedEscape NvimStringSpecial",
    613 
    614  "default link NvimFigureBrace NvimInternalError",
    615  "default link NvimSingleQuotedUnknownEscape NvimInternalError",
    616 
    617  "default link NvimSpacing Normal",
    618 
    619  // NvimInvalid groups:
    620 
    621  "default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError",
    622 
    623  "default link NvimInvalid Error",
    624 
    625  "default link NvimInvalidAssignment NvimInvalid",
    626  "default link NvimInvalidPlainAssignment NvimInvalidAssignment",
    627  "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment",
    628  "default link NvimInvalidAssignmentWithAddition NvimInvalidAugmentedAssignment",
    629  "default link NvimInvalidAssignmentWithSubtraction NvimInvalidAugmentedAssignment",
    630  "default link NvimInvalidAssignmentWithConcatenation NvimInvalidAugmentedAssignment",
    631 
    632  "default link NvimInvalidOperator NvimInvalid",
    633 
    634  "default link NvimInvalidUnaryOperator NvimInvalidOperator",
    635  "default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator",
    636  "default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator",
    637  "default link NvimInvalidNot NvimInvalidUnaryOperator",
    638 
    639  "default link NvimInvalidBinaryOperator NvimInvalidOperator",
    640  "default link NvimInvalidComparison NvimInvalidBinaryOperator",
    641  "default link NvimInvalidComparisonModifier NvimInvalidComparison",
    642  "default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator",
    643  "default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator",
    644  "default link NvimInvalidConcat NvimInvalidBinaryOperator",
    645  "default link NvimInvalidConcatOrSubscript NvimInvalidConcat",
    646  "default link NvimInvalidOr NvimInvalidBinaryOperator",
    647  "default link NvimInvalidAnd NvimInvalidBinaryOperator",
    648  "default link NvimInvalidMultiplication NvimInvalidBinaryOperator",
    649  "default link NvimInvalidDivision NvimInvalidBinaryOperator",
    650  "default link NvimInvalidMod NvimInvalidBinaryOperator",
    651 
    652  "default link NvimInvalidTernary NvimInvalidOperator",
    653  "default link NvimInvalidTernaryColon NvimInvalidTernary",
    654 
    655  "default link NvimInvalidDelimiter NvimInvalid",
    656 
    657  "default link NvimInvalidParenthesis NvimInvalidDelimiter",
    658  "default link NvimInvalidLambda NvimInvalidParenthesis",
    659  "default link NvimInvalidNestingParenthesis NvimInvalidParenthesis",
    660  "default link NvimInvalidCallingParenthesis NvimInvalidParenthesis",
    661 
    662  "default link NvimInvalidSubscript NvimInvalidParenthesis",
    663  "default link NvimInvalidSubscriptBracket NvimInvalidSubscript",
    664  "default link NvimInvalidSubscriptColon NvimInvalidSubscript",
    665  "default link NvimInvalidCurly NvimInvalidSubscript",
    666 
    667  "default link NvimInvalidContainer NvimInvalidParenthesis",
    668  "default link NvimInvalidDict NvimInvalidContainer",
    669  "default link NvimInvalidList NvimInvalidContainer",
    670 
    671  "default link NvimInvalidValue NvimInvalid",
    672 
    673  "default link NvimInvalidIdentifier NvimInvalidValue",
    674  "default link NvimInvalidIdentifierScope NvimInvalidIdentifier",
    675  "default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier",
    676  "default link NvimInvalidIdentifierName NvimInvalidIdentifier",
    677  "default link NvimInvalidIdentifierKey NvimInvalidIdentifier",
    678 
    679  "default link NvimInvalidColon NvimInvalidDelimiter",
    680  "default link NvimInvalidComma NvimInvalidDelimiter",
    681  "default link NvimInvalidArrow NvimInvalidDelimiter",
    682 
    683  "default link NvimInvalidRegister NvimInvalidValue",
    684  "default link NvimInvalidNumber NvimInvalidValue",
    685  "default link NvimInvalidFloat NvimInvalidNumber",
    686  "default link NvimInvalidNumberPrefix NvimInvalidNumber",
    687 
    688  "default link NvimInvalidOptionSigil NvimInvalidIdentifier",
    689  "default link NvimInvalidOptionName NvimInvalidIdentifier",
    690  "default link NvimInvalidOptionScope NvimInvalidIdentifierScope",
    691  "default link NvimInvalidOptionScopeDelimiter NvimInvalidIdentifierScopeDelimiter",
    692 
    693  "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil",
    694  "default link NvimInvalidEnvironmentName NvimInvalidIdentifier",
    695 
    696  // Invalid string bodies and specials are still highlighted as valid ones to
    697  // minimize the red area.
    698  "default link NvimInvalidString NvimInvalidValue",
    699  "default link NvimInvalidStringBody NvimStringBody",
    700  "default link NvimInvalidStringQuote NvimInvalidString",
    701  "default link NvimInvalidStringSpecial NvimStringSpecial",
    702 
    703  "default link NvimInvalidSingleQuote NvimInvalidStringQuote",
    704  "default link NvimInvalidSingleQuotedBody NvimInvalidStringBody",
    705  "default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial",
    706 
    707  "default link NvimInvalidDoubleQuote NvimInvalidStringQuote",
    708  "default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody",
    709  "default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial",
    710  "default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue",
    711 
    712  "default link NvimInvalidFigureBrace NvimInvalidDelimiter",
    713 
    714  "default link NvimInvalidSpacing ErrorMsg",
    715 
    716  // Not actually invalid, but we show the user that they are doing something
    717  // wrong.
    718  "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue",
    719  NULL,
    720 };
    721 
    722 /// Returns the number of highlight groups.
    723 int highlight_num_groups(void)
    724 {
    725  return highlight_ga.ga_len;
    726 }
    727 
    728 /// Returns the name of a highlight group.
    729 char *highlight_group_name(int id)
    730 {
    731  return hl_table[id].sg_name;
    732 }
    733 
    734 /// Returns the ID of the link to a highlight group.
    735 int highlight_link_id(int id)
    736 {
    737  return hl_table[id].sg_link;
    738 }
    739 
    740 /// Create default links for Nvim* highlight groups used for cmdline coloring
    741 void syn_init_cmdline_highlight(bool reset, bool init)
    742 {
    743  for (size_t i = 0; highlight_init_cmdline[i] != NULL; i++) {
    744    do_highlight(highlight_init_cmdline[i], reset, init);
    745  }
    746 }
    747 
    748 /// Load colors from a file if "g:colors_name" is set, otherwise load builtin
    749 /// colors
    750 ///
    751 /// @param both include groups where 'bg' doesn't matter
    752 /// @param reset clear groups first
    753 void init_highlight(bool both, bool reset)
    754 {
    755  static bool had_both = false;
    756 
    757  // Try finding the color scheme file.  Used when a color file was loaded
    758  // and 'background' or 't_Co' is changed.
    759  char *p = get_var_value("g:colors_name");
    760  if (p != NULL) {
    761    // Value of g:colors_name could be freed in load_colors() and make
    762    // p invalid, so copy it.
    763    char *copy_p = xstrdup(p);
    764    bool okay = load_colors(copy_p);
    765    xfree(copy_p);
    766    if (okay) {
    767      return;
    768    }
    769  }
    770 
    771  // Didn't use a color file, use the compiled-in colors.
    772  if (both) {
    773    had_both = true;
    774    const char *const *const pp = highlight_init_both;
    775    for (size_t i = 0; pp[i] != NULL; i++) {
    776      do_highlight(pp[i], reset, true);
    777    }
    778  } else if (!had_both) {
    779    // Don't do anything before the call with both == true from main().
    780    // Not everything has been setup then, and that call will overrule
    781    // everything anyway.
    782    return;
    783  }
    784 
    785  const char *const *const pp = ((*p_bg == 'l')
    786                                 ? highlight_init_light
    787                                 : highlight_init_dark);
    788  for (size_t i = 0; pp[i] != NULL; i++) {
    789    do_highlight(pp[i], reset, true);
    790  }
    791 
    792  syn_init_cmdline_highlight(false, false);
    793 }
    794 
    795 /// Load color file "name".
    796 ///
    797 /// @return  OK for success, FAIL for failure.
    798 int load_colors(char *name)
    799 {
    800  static bool recursive = false;
    801 
    802  // When being called recursively, this is probably because setting
    803  // 'background' caused the highlighting to be reloaded.  This means it is
    804  // working, thus we should return OK.
    805  if (recursive) {
    806    return OK;
    807  }
    808 
    809  recursive = true;
    810  size_t buflen = strlen(name) + 12;
    811  char *buf = xmalloc(buflen);
    812  apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf);
    813  snprintf(buf, buflen, "colors/%s.*", name);
    814  int retval = source_runtime_vim_lua(buf, DIP_START + DIP_OPT);
    815  xfree(buf);
    816  if (retval == OK) {
    817    apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, false, curbuf);
    818  }
    819 
    820  recursive = false;
    821 
    822  return retval;
    823 }
    824 
    825 static char *(color_names[28]) = {
    826  "Black", "DarkBlue", "DarkGreen", "DarkCyan",
    827  "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
    828  "Gray", "Grey", "LightGray", "LightGrey",
    829  "DarkGray", "DarkGrey",
    830  "Blue", "LightBlue", "Green", "LightGreen",
    831  "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
    832  "LightMagenta", "Yellow", "LightYellow", "White", "NONE"
    833 };
    834 // indices:
    835 // 0, 1, 2, 3,
    836 // 4, 5, 6, 7,
    837 // 8, 9, 10, 11,
    838 // 12, 13,
    839 // 14, 15, 16, 17,
    840 // 18, 19, 20, 21, 22,
    841 // 23, 24, 25, 26, 27
    842 static int color_numbers_16[28] = { 0, 1, 2, 3,
    843                                    4, 5, 6, 6,
    844                                    7, 7, 7, 7,
    845                                    8, 8,
    846                                    9, 9, 10, 10,
    847                                    11, 11, 12, 12, 13,
    848                                    13, 14, 14, 15, -1 };
    849 // for xterm with 88 colors...
    850 static int color_numbers_88[28] = { 0, 4, 2, 6,
    851                                    1, 5, 32, 72,
    852                                    84, 84, 7, 7,
    853                                    82, 82,
    854                                    12, 43, 10, 61,
    855                                    14, 63, 9, 74, 13,
    856                                    75, 11, 78, 15, -1 };
    857 // for xterm with 256 colors...
    858 static int color_numbers_256[28] = { 0, 4, 2, 6,
    859                                     1, 5, 130, 3,
    860                                     248, 248, 7, 7,
    861                                     242, 242,
    862                                     12, 81, 10, 121,
    863                                     14, 159, 9, 224, 13,
    864                                     225, 11, 229, 15, -1 };
    865 // for terminals with less than 16 colors...
    866 static int color_numbers_8[28] = { 0, 4, 2, 6,
    867                                   1, 5, 3, 3,
    868                                   7, 7, 7, 7,
    869                                   0 + 8, 0 + 8,
    870                                   4 + 8, 4 + 8, 2 + 8, 2 + 8,
    871                                   6 + 8, 6 + 8, 1 + 8, 1 + 8, 5 + 8,
    872                                   5 + 8, 3 + 8, 3 + 8, 7 + 8, -1 };
    873 
    874 // Lookup the "cterm" value to be used for color with index "idx" in
    875 // color_names[].
    876 // "boldp" will be set to kTrue or kFalse for a foreground color when using 8
    877 // colors, otherwise it will be unchanged.
    878 static int lookup_color(const int idx, const bool foreground, TriState *const boldp)
    879 {
    880  int color = color_numbers_16[idx];
    881 
    882  // Use the _16 table to check if it's a valid color name.
    883  if (color < 0) {
    884    return -1;
    885  }
    886 
    887  if (t_colors == 8) {
    888    // t_Co is 8: use the 8 colors table
    889    color = color_numbers_8[idx];
    890    if (foreground) {
    891      // set/reset bold attribute to get light foreground
    892      // colors (on some terminals, e.g. "linux")
    893      if (color & 8) {
    894        *boldp = kTrue;
    895      } else {
    896        *boldp = kFalse;
    897      }
    898    }
    899    color &= 7;   // truncate to 8 colors
    900  } else if (t_colors == 16) {
    901    color = color_numbers_8[idx];
    902  } else if (t_colors == 88) {
    903    color = color_numbers_88[idx];
    904  } else if (t_colors >= 256) {
    905    color = color_numbers_256[idx];
    906  }
    907  return color;
    908 }
    909 
    910 void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
    911 {
    912  int idx = id - 1;  // Index is ID minus one.
    913  bool is_default = attrs.rgb_ae_attr & HL_DEFAULT;
    914 
    915  // Return if "default" was used and the group already has settings
    916  if (is_default && hl_has_settings(idx, true) && !dict->force) {
    917    return;
    918  }
    919 
    920  HlGroup *g = &hl_table[idx];
    921  g->sg_cleared = false;
    922 
    923  if (link_id > 0) {
    924    g->sg_link = link_id;
    925    g->sg_script_ctx = current_sctx;
    926    g->sg_script_ctx.sc_lnum += SOURCING_LNUM;
    927    nlua_set_sctx(&g->sg_script_ctx);
    928    g->sg_set |= SG_LINK;
    929    if (is_default) {
    930      g->sg_deflink = link_id;
    931      g->sg_deflink_sctx = current_sctx;
    932      g->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
    933      nlua_set_sctx(&g->sg_deflink_sctx);
    934    }
    935  } else {
    936    g->sg_link = 0;
    937  }
    938 
    939  g->sg_gui = attrs.rgb_ae_attr &~HL_DEFAULT;
    940 
    941  g->sg_rgb_fg = attrs.rgb_fg_color;
    942  g->sg_rgb_bg = attrs.rgb_bg_color;
    943  g->sg_rgb_sp = attrs.rgb_sp_color;
    944 
    945  struct {
    946    int *dest; RgbValue val; Object name;
    947  } cattrs[] = {
    948    { &g->sg_rgb_fg_idx, g->sg_rgb_fg,
    949      HAS_KEY(dict, highlight, fg) ? dict->fg : dict->foreground },
    950    { &g->sg_rgb_bg_idx, g->sg_rgb_bg,
    951      HAS_KEY(dict, highlight, bg) ? dict->bg : dict->background },
    952    { &g->sg_rgb_sp_idx, g->sg_rgb_sp, HAS_KEY(dict, highlight, sp) ? dict->sp : dict->special },
    953    { NULL, -1, NIL },
    954  };
    955 
    956  for (int j = 0; cattrs[j].dest; j++) {
    957    if (cattrs[j].val < 0) {
    958      *cattrs[j].dest = kColorIdxNone;
    959    } else if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) {
    960      name_to_color(cattrs[j].name.data.string.data, cattrs[j].dest);
    961    } else {
    962      *cattrs[j].dest = kColorIdxHex;
    963    }
    964  }
    965 
    966  g->sg_cterm = attrs.cterm_ae_attr &~HL_DEFAULT;
    967  g->sg_cterm_bg = attrs.cterm_bg_color;
    968  g->sg_cterm_fg = attrs.cterm_fg_color;
    969  g->sg_cterm_bold = g->sg_cterm & HL_BOLD;
    970  g->sg_blend = attrs.hl_blend;
    971 
    972  g->sg_script_ctx = current_sctx;
    973  g->sg_script_ctx.sc_lnum += SOURCING_LNUM;
    974  nlua_set_sctx(&g->sg_script_ctx);
    975 
    976  g->sg_attr = hl_get_syn_attr(0, id, attrs);
    977 
    978  // 'Normal' is special
    979  if (strcmp(g->sg_name_u, "NORMAL") == 0) {
    980    cterm_normal_fg_color = g->sg_cterm_fg;
    981    cterm_normal_bg_color = g->sg_cterm_bg;
    982    bool did_changed = false;
    983    if (normal_bg != g->sg_rgb_bg || normal_fg != g->sg_rgb_fg || normal_sp != g->sg_rgb_sp) {
    984      did_changed = true;
    985    }
    986    normal_fg = g->sg_rgb_fg;
    987    normal_bg = g->sg_rgb_bg;
    988    normal_sp = g->sg_rgb_sp;
    989 
    990    if (did_changed) {
    991      highlight_attr_set_all();
    992    }
    993    ui_default_colors_set();
    994  } else {
    995    // a cursor style uses this syn_id, make sure its attribute is updated.
    996    if (cursor_mode_uses_syn_id(id)) {
    997      ui_mode_info_set();
    998    }
    999  }
   1000 
   1001  if (!updating_screen) {
   1002    redraw_all_later(UPD_NOT_VALID);
   1003  }
   1004  need_highlight_changed = true;
   1005 }
   1006 
   1007 /// Handle ":highlight" command
   1008 ///
   1009 /// When using ":highlight clear" this is called recursively for each group with
   1010 /// forceit and init being both true.
   1011 ///
   1012 /// @param[in]  line  Command arguments.
   1013 /// @param[in]  forceit  True when bang is given, allows to link group even if
   1014 ///                      it has its own settings.
   1015 /// @param[in]  init  True when initializing.
   1016 void do_highlight(const char *line, const bool forceit, const bool init)
   1017  FUNC_ATTR_NONNULL_ALL
   1018 {
   1019  // If no argument, list current highlighting.
   1020  if (!init && ends_excmd((uint8_t)(*line))) {
   1021    msg_ext_set_kind("list_cmd");
   1022    for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
   1023      // TODO(brammool): only call when the group has attributes set
   1024      highlight_list_one(i);
   1025    }
   1026    return;
   1027  }
   1028 
   1029  bool dodefault = false;
   1030 
   1031  // Isolate the name.
   1032  const char *name_end = skiptowhite(line);
   1033  const char *linep = skipwhite(name_end);
   1034 
   1035  // Check for "default" argument.
   1036  if (strncmp(line, "default", (size_t)(name_end - line)) == 0) {
   1037    dodefault = true;
   1038    line = linep;
   1039    name_end = skiptowhite(line);
   1040    linep = skipwhite(name_end);
   1041  }
   1042 
   1043  bool doclear = false;
   1044  bool dolink = false;
   1045 
   1046  // Check for "clear" or "link" argument.
   1047  if (strncmp(line, "clear", (size_t)(name_end - line)) == 0) {
   1048    doclear = true;
   1049  } else if (strncmp(line, "link", (size_t)(name_end - line)) == 0) {
   1050    dolink = true;
   1051  }
   1052 
   1053  // ":highlight {group-name}": list highlighting for one group.
   1054  if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) {
   1055    int id = syn_name2id_len(line, (size_t)(name_end - line));
   1056    if (id == 0) {
   1057      semsg(_(e_highlight_group_name_not_found_str), line);
   1058    } else {
   1059      msg_ext_set_kind("list_cmd");
   1060      highlight_list_one(id);
   1061    }
   1062    return;
   1063  }
   1064 
   1065  // Handle ":highlight link {from} {to}" command.
   1066  if (dolink) {
   1067    const char *from_start = linep;
   1068    int to_id;
   1069    HlGroup *hlgroup = NULL;
   1070 
   1071    const char *from_end = skiptowhite(from_start);
   1072    const char *to_start = skipwhite(from_end);
   1073    const char *to_end = skiptowhite(to_start);
   1074 
   1075    if (ends_excmd((uint8_t)(*from_start))
   1076        || ends_excmd((uint8_t)(*to_start))) {
   1077      semsg(_("E412: Not enough arguments: \":highlight link %s\""),
   1078            from_start);
   1079      return;
   1080    }
   1081 
   1082    if (!ends_excmd(*skipwhite(to_end))) {
   1083      semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
   1084      return;
   1085    }
   1086 
   1087    int from_id = syn_check_group(from_start, (size_t)(from_end - from_start));
   1088    if (strncmp(to_start, "NONE", 4) == 0) {
   1089      to_id = 0;
   1090    } else {
   1091      to_id = syn_check_group(to_start, (size_t)(to_end - to_start));
   1092    }
   1093 
   1094    if (from_id > 0) {
   1095      hlgroup = &hl_table[from_id - 1];
   1096      if (dodefault && (forceit || hlgroup->sg_deflink == 0)) {
   1097        hlgroup->sg_deflink = to_id;
   1098        hlgroup->sg_deflink_sctx = current_sctx;
   1099        hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
   1100        nlua_set_sctx(&hlgroup->sg_deflink_sctx);
   1101      }
   1102    }
   1103 
   1104    if (from_id > 0 && (!init || hlgroup->sg_set == 0)) {
   1105      // Don't allow a link when there already is some highlighting
   1106      // for the group, unless '!' is used
   1107      if (to_id > 0 && !forceit && !init
   1108          && hl_has_settings(from_id - 1, dodefault)) {
   1109        if (SOURCING_NAME == NULL && !dodefault) {
   1110          emsg(_(e_group_has_settings_highlight_link_ignored));
   1111        }
   1112      } else if (hlgroup->sg_link != to_id
   1113                 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
   1114                 || hlgroup->sg_cleared) {
   1115        if (!init) {
   1116          hlgroup->sg_set |= SG_LINK;
   1117        }
   1118        hlgroup->sg_link = to_id;
   1119        hlgroup->sg_script_ctx = current_sctx;
   1120        hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
   1121        nlua_set_sctx(&hlgroup->sg_script_ctx);
   1122        hlgroup->sg_cleared = false;
   1123        redraw_all_later(UPD_SOME_VALID);
   1124 
   1125        // Only call highlight changed() once after multiple changes
   1126        need_highlight_changed = true;
   1127      }
   1128    }
   1129 
   1130    return;
   1131  }
   1132 
   1133  if (doclear) {
   1134    // ":highlight clear [group]" command.
   1135    line = linep;
   1136    if (ends_excmd((uint8_t)(*line))) {
   1137      do_unlet(S_LEN("g:colors_name"), true);
   1138      restore_cterm_colors();
   1139 
   1140      // Clear all default highlight groups and load the defaults.
   1141      for (int j = 0; j < highlight_ga.ga_len; j++) {
   1142        highlight_clear(j);
   1143      }
   1144      init_highlight(true, true);
   1145      highlight_changed();
   1146      redraw_all_later(UPD_NOT_VALID);
   1147      return;
   1148    }
   1149    name_end = skiptowhite(line);
   1150    linep = skipwhite(name_end);
   1151  }
   1152 
   1153  // Find the group name in the table.  If it does not exist yet, add it.
   1154  int id = syn_check_group(line, (size_t)(name_end - line));
   1155  if (id == 0) {  // Failed (out of memory).
   1156    return;
   1157  }
   1158  int idx = id - 1;  // Index is ID minus one.
   1159 
   1160  // Return if "default" was used and the group already has settings
   1161  if (dodefault && hl_has_settings(idx, true)) {
   1162    return;
   1163  }
   1164 
   1165  // Make a copy so we can check if any attribute actually changed
   1166  HlGroup item_before = hl_table[idx];
   1167  bool is_normal_group = (strcmp(hl_table[idx].sg_name_u, "NORMAL") == 0);
   1168 
   1169  // Clear the highlighting for ":hi clear {group}" and ":hi clear".
   1170  if (doclear || (forceit && init)) {
   1171    highlight_clear(idx);
   1172    if (!doclear) {
   1173      hl_table[idx].sg_set = 0;
   1174    }
   1175  }
   1176 
   1177  bool did_change = false;
   1178  bool error = false;
   1179 
   1180  char key[64];
   1181  char arg[512];
   1182  if (!doclear) {
   1183    const char *arg_start;
   1184 
   1185    while (!ends_excmd((uint8_t)(*linep))) {
   1186      const char *key_start = linep;
   1187      if (*linep == '=') {
   1188        semsg(_(e_unexpected_equal_sign_str), key_start);
   1189        error = true;
   1190        break;
   1191      }
   1192 
   1193      // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg",
   1194      // "guibg" or "guisp").
   1195      while (*linep && !ascii_iswhite(*linep) && *linep != '=') {
   1196        linep++;
   1197      }
   1198      size_t key_len = (size_t)(linep - key_start);
   1199      if (key_len > sizeof(key) - 1) {
   1200        emsg(_("E423: Illegal argument"));
   1201        error = true;
   1202        break;
   1203      }
   1204      vim_memcpy_up(key, key_start, key_len);
   1205      key[key_len] = NUL;
   1206      linep = skipwhite(linep);
   1207 
   1208      if (strcmp(key, "NONE") == 0) {
   1209        if (!init || hl_table[idx].sg_set == 0) {
   1210          if (!init) {
   1211            hl_table[idx].sg_set |= SG_CTERM + SG_GUI;
   1212          }
   1213          highlight_clear(idx);
   1214        }
   1215        continue;
   1216      }
   1217 
   1218      // Check for the equal sign.
   1219      if (*linep != '=') {
   1220        semsg(_(e_missing_equal_sign_str_2), key_start);
   1221        error = true;
   1222        break;
   1223      }
   1224      linep++;
   1225 
   1226      // Isolate the argument.
   1227      linep = skipwhite(linep);
   1228      if (*linep == '\'') {  // guifg='color name'
   1229        arg_start = ++linep;
   1230        linep = strchr(linep, '\'');
   1231        if (linep == NULL) {
   1232          semsg(_(e_invarg2), key_start);
   1233          error = true;
   1234          break;
   1235        }
   1236      } else {
   1237        arg_start = linep;
   1238        linep = skiptowhite(linep);
   1239      }
   1240      if (linep == arg_start) {
   1241        semsg(_(e_missing_argument_str), key_start);
   1242        error = true;
   1243        break;
   1244      }
   1245      size_t arg_len = (size_t)(linep - arg_start);
   1246      if (arg_len > sizeof(arg) - 1) {
   1247        emsg(_("E423: Illegal argument"));
   1248        error = true;
   1249        break;
   1250      }
   1251      memcpy(arg, arg_start, arg_len);
   1252      arg[arg_len] = NUL;
   1253 
   1254      if (*linep == '\'') {
   1255        linep++;
   1256      }
   1257 
   1258      // Store the argument.
   1259      if (strcmp(key, "TERM") == 0
   1260          || strcmp(key, "CTERM") == 0
   1261          || strcmp(key, "GUI") == 0) {
   1262        int attr = 0;
   1263        int off = 0;
   1264        int i;
   1265        while (arg[off] != NUL) {
   1266          for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) {
   1267            int len = (int)strlen(hl_name_table[i]);
   1268            if (STRNICMP(arg + off, hl_name_table[i], len) == 0) {
   1269              if (hl_attr_table[i] & HL_UNDERLINE_MASK) {
   1270                attr &= ~HL_UNDERLINE_MASK;
   1271              }
   1272              attr |= hl_attr_table[i];
   1273              off += len;
   1274              break;
   1275            }
   1276          }
   1277          if (i < 0) {
   1278            semsg(_("E418: Illegal value: %s"), arg);
   1279            error = true;
   1280            break;
   1281          }
   1282          if (arg[off] == ',') {  // Another one follows.
   1283            off++;
   1284          }
   1285        }
   1286        if (error) {
   1287          break;
   1288        }
   1289        if (*key == 'C') {
   1290          if (!init || !(hl_table[idx].sg_set & SG_CTERM)) {
   1291            if (!init) {
   1292              hl_table[idx].sg_set |= SG_CTERM;
   1293            }
   1294            hl_table[idx].sg_cterm = attr;
   1295            hl_table[idx].sg_cterm_bold = false;
   1296          }
   1297        } else if (*key == 'G') {
   1298          if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
   1299            if (!init) {
   1300              hl_table[idx].sg_set |= SG_GUI;
   1301            }
   1302            hl_table[idx].sg_gui = attr;
   1303          }
   1304        }
   1305      } else if (strcmp(key, "FONT") == 0) {
   1306        // in non-GUI fonts are simply ignored
   1307      } else if (strcmp(key, "CTERMFG") == 0 || strcmp(key, "CTERMBG") == 0) {
   1308        if (!init || !(hl_table[idx].sg_set & SG_CTERM)) {
   1309          if (!init) {
   1310            hl_table[idx].sg_set |= SG_CTERM;
   1311          }
   1312 
   1313          // When setting the foreground color, and previously the "bold"
   1314          // flag was set for a light color, reset it now
   1315          if (key[5] == 'F' && hl_table[idx].sg_cterm_bold) {
   1316            hl_table[idx].sg_cterm &= ~HL_BOLD;
   1317            hl_table[idx].sg_cterm_bold = false;
   1318          }
   1319 
   1320          int color;
   1321          if (ascii_isdigit(*arg)) {
   1322            color = atoi(arg);
   1323          } else if (STRICMP(arg, "fg") == 0) {
   1324            if (cterm_normal_fg_color) {
   1325              color = cterm_normal_fg_color - 1;
   1326            } else {
   1327              emsg(_("E419: FG color unknown"));
   1328              error = true;
   1329              break;
   1330            }
   1331          } else if (STRICMP(arg, "bg") == 0) {
   1332            if (cterm_normal_bg_color > 0) {
   1333              color = cterm_normal_bg_color - 1;
   1334            } else {
   1335              emsg(_("E420: BG color unknown"));
   1336              error = true;
   1337              break;
   1338            }
   1339          } else {
   1340            // Reduce calls to STRICMP a bit, it can be slow.
   1341            int off = TOUPPER_ASC(*arg);
   1342            int i;
   1343            for (i = ARRAY_SIZE(color_names); --i >= 0;) {
   1344              if (off == color_names[i][0]
   1345                  && STRICMP(arg + 1, color_names[i] + 1) == 0) {
   1346                break;
   1347              }
   1348            }
   1349            if (i < 0) {
   1350              semsg(_("E421: Color name or number not recognized: %s"),
   1351                    key_start);
   1352              error = true;
   1353              break;
   1354            }
   1355 
   1356            TriState bold = kNone;
   1357            color = lookup_color(i, key[5] == 'F', &bold);
   1358 
   1359            // set/reset bold attribute to get light foreground
   1360            // colors (on some terminals, e.g. "linux")
   1361            if (bold == kTrue) {
   1362              hl_table[idx].sg_cterm |= HL_BOLD;
   1363              hl_table[idx].sg_cterm_bold = true;
   1364            } else if (bold == kFalse) {
   1365              hl_table[idx].sg_cterm &= ~HL_BOLD;
   1366            }
   1367          }
   1368          // Add one to the argument, to avoid zero.  Zero is used for
   1369          // "NONE", then "color" is -1.
   1370          if (key[5] == 'F') {
   1371            hl_table[idx].sg_cterm_fg = color + 1;
   1372            if (is_normal_group) {
   1373              cterm_normal_fg_color = color + 1;
   1374            }
   1375          } else {
   1376            hl_table[idx].sg_cterm_bg = color + 1;
   1377            if (is_normal_group) {
   1378              cterm_normal_bg_color = color + 1;
   1379              if (!ui_rgb_attached()) {
   1380                if (color >= 0) {
   1381                  int dark = -1;
   1382 
   1383                  if (t_colors < 16) {
   1384                    dark = (color == 0 || color == 4);
   1385                  } else if (color < 16) {
   1386                    // Limit the heuristic to the standard 16 colors
   1387                    dark = (color < 7 || color == 8);
   1388                  }
   1389                  // Set the 'background' option if the value is
   1390                  // wrong.
   1391                  if (dark != -1
   1392                      && dark != (*p_bg == 'd')
   1393                      && !option_was_set(kOptBackground)) {
   1394                    set_option_value_give_err(kOptBackground,
   1395                                              CSTR_AS_OPTVAL(dark ? "dark" : "light"), 0);
   1396                    reset_option_was_set(kOptBackground);
   1397                  }
   1398                }
   1399              }
   1400            }
   1401          }
   1402        }
   1403      } else if (strcmp(key, "GUIFG") == 0) {
   1404        int *indexp = &hl_table[idx].sg_rgb_fg_idx;
   1405 
   1406        if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
   1407          if (!init) {
   1408            hl_table[idx].sg_set |= SG_GUI;
   1409          }
   1410 
   1411          RgbValue old_color = hl_table[idx].sg_rgb_fg;
   1412          int old_idx = hl_table[idx].sg_rgb_fg_idx;
   1413 
   1414          if (strcmp(arg, "NONE") != 0) {
   1415            hl_table[idx].sg_rgb_fg = name_to_color(arg, indexp);
   1416          } else {
   1417            hl_table[idx].sg_rgb_fg = -1;
   1418            hl_table[idx].sg_rgb_fg_idx = kColorIdxNone;
   1419          }
   1420 
   1421          did_change = hl_table[idx].sg_rgb_fg != old_color || hl_table[idx].sg_rgb_fg != old_idx;
   1422        }
   1423 
   1424        if (is_normal_group) {
   1425          normal_fg = hl_table[idx].sg_rgb_fg;
   1426        }
   1427      } else if (strcmp(key, "GUIBG") == 0) {
   1428        int *indexp = &hl_table[idx].sg_rgb_bg_idx;
   1429 
   1430        if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
   1431          if (!init) {
   1432            hl_table[idx].sg_set |= SG_GUI;
   1433          }
   1434 
   1435          RgbValue old_color = hl_table[idx].sg_rgb_bg;
   1436          int old_idx = hl_table[idx].sg_rgb_bg_idx;
   1437 
   1438          if (strcmp(arg, "NONE") != 0) {
   1439            hl_table[idx].sg_rgb_bg = name_to_color(arg, indexp);
   1440          } else {
   1441            hl_table[idx].sg_rgb_bg = -1;
   1442            hl_table[idx].sg_rgb_bg_idx = kColorIdxNone;
   1443          }
   1444 
   1445          did_change = hl_table[idx].sg_rgb_bg != old_color || hl_table[idx].sg_rgb_bg != old_idx;
   1446        }
   1447 
   1448        if (is_normal_group) {
   1449          normal_bg = hl_table[idx].sg_rgb_bg;
   1450        }
   1451      } else if (strcmp(key, "GUISP") == 0) {
   1452        int *indexp = &hl_table[idx].sg_rgb_sp_idx;
   1453 
   1454        if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
   1455          if (!init) {
   1456            hl_table[idx].sg_set |= SG_GUI;
   1457          }
   1458 
   1459          RgbValue old_color = hl_table[idx].sg_rgb_sp;
   1460          int old_idx = hl_table[idx].sg_rgb_sp_idx;
   1461 
   1462          if (strcmp(arg, "NONE") != 0) {
   1463            hl_table[idx].sg_rgb_sp = name_to_color(arg, indexp);
   1464          } else {
   1465            hl_table[idx].sg_rgb_sp = -1;
   1466          }
   1467 
   1468          did_change = hl_table[idx].sg_rgb_sp != old_color || hl_table[idx].sg_rgb_sp != old_idx;
   1469        }
   1470 
   1471        if (is_normal_group) {
   1472          normal_sp = hl_table[idx].sg_rgb_sp;
   1473        }
   1474      } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) {
   1475        // Ignored for now
   1476      } else if (strcmp(key, "BLEND") == 0) {
   1477        if (strcmp(arg, "NONE") != 0) {
   1478          hl_table[idx].sg_blend = (int)strtol(arg, NULL, 10);
   1479        } else {
   1480          hl_table[idx].sg_blend = -1;
   1481        }
   1482      } else {
   1483        semsg(_("E423: Illegal argument: %s"), key_start);
   1484        error = true;
   1485        break;
   1486      }
   1487      hl_table[idx].sg_cleared = false;
   1488 
   1489      // When highlighting has been given for a group, don't link it.
   1490      if (!init || !(hl_table[idx].sg_set & SG_LINK)) {
   1491        hl_table[idx].sg_link = 0;
   1492      }
   1493 
   1494      // Continue with next argument.
   1495      linep = skipwhite(linep);
   1496    }
   1497  }
   1498 
   1499  bool did_highlight_changed = false;
   1500 
   1501  if (!error && is_normal_group) {
   1502    // Need to update all groups, because they might be using "bg" and/or
   1503    // "fg", which have been changed now.
   1504    highlight_attr_set_all();
   1505 
   1506    if (!ui_has(kUILinegrid) && starting == 0) {
   1507      // Older UIs assume that we clear the screen after normal group is
   1508      // changed
   1509      ui_refresh();
   1510    } else {
   1511      // TUI and newer UIs will repaint the screen themselves. UPD_NOT_VALID
   1512      // redraw below will still handle usages of guibg=fg etc.
   1513      ui_default_colors_set();
   1514    }
   1515    did_highlight_changed = true;
   1516    redraw_all_later(UPD_NOT_VALID);
   1517  } else {
   1518    set_hl_attr(idx);
   1519  }
   1520  hl_table[idx].sg_script_ctx = current_sctx;
   1521  hl_table[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
   1522  nlua_set_sctx(&hl_table[idx].sg_script_ctx);
   1523 
   1524  // Only call highlight_changed() once, after a sequence of highlight
   1525  // commands, and only if an attribute actually changed
   1526  if ((did_change
   1527       || memcmp(&hl_table[idx], &item_before, sizeof(item_before)) != 0)
   1528      && !did_highlight_changed) {
   1529    // Do not trigger a redraw when highlighting is changed while
   1530    // redrawing.  This may happen when evaluating 'statusline' changes the
   1531    // StatusLine group.
   1532    if (!updating_screen) {
   1533      redraw_all_later(UPD_NOT_VALID);
   1534    }
   1535    need_highlight_changed = true;
   1536  }
   1537 }
   1538 
   1539 #if defined(EXITFREE)
   1540 void free_highlight(void)
   1541 {
   1542  ga_clear(&highlight_ga);
   1543  map_destroy(cstr_t, &highlight_unames);
   1544  arena_mem_free(arena_finish(&highlight_arena));
   1545 }
   1546 
   1547 #endif
   1548 
   1549 /// Reset the cterm colors to what they were before Vim was started, if
   1550 /// possible.  Otherwise reset them to zero.
   1551 void restore_cterm_colors(void)
   1552 {
   1553  normal_fg = -1;
   1554  normal_bg = -1;
   1555  normal_sp = -1;
   1556  cterm_normal_fg_color = 0;
   1557  cterm_normal_bg_color = 0;
   1558 }
   1559 
   1560 /// @param check_link  if true also check for an existing link.
   1561 ///
   1562 /// @return true if highlight group "idx" has any settings.
   1563 static bool hl_has_settings(int idx, bool check_link)
   1564 {
   1565  return hl_table[idx].sg_cleared == 0
   1566         && (hl_table[idx].sg_attr != 0
   1567             || hl_table[idx].sg_cterm_fg != 0
   1568             || hl_table[idx].sg_cterm_bg != 0
   1569             || hl_table[idx].sg_rgb_fg_idx != kColorIdxNone
   1570             || hl_table[idx].sg_rgb_bg_idx != kColorIdxNone
   1571             || hl_table[idx].sg_rgb_sp_idx != kColorIdxNone
   1572             || (check_link && (hl_table[idx].sg_set & SG_LINK)));
   1573 }
   1574 
   1575 /// Clear highlighting for one group.
   1576 static void highlight_clear(int idx)
   1577 {
   1578  hl_table[idx].sg_cleared = true;
   1579 
   1580  hl_table[idx].sg_attr = 0;
   1581  hl_table[idx].sg_cterm = 0;
   1582  hl_table[idx].sg_cterm_bold = false;
   1583  hl_table[idx].sg_cterm_fg = 0;
   1584  hl_table[idx].sg_cterm_bg = 0;
   1585  hl_table[idx].sg_gui = 0;
   1586  hl_table[idx].sg_rgb_fg = -1;
   1587  hl_table[idx].sg_rgb_bg = -1;
   1588  hl_table[idx].sg_rgb_sp = -1;
   1589  hl_table[idx].sg_rgb_fg_idx = kColorIdxNone;
   1590  hl_table[idx].sg_rgb_bg_idx = kColorIdxNone;
   1591  hl_table[idx].sg_rgb_sp_idx = kColorIdxNone;
   1592  hl_table[idx].sg_blend = -1;
   1593  // Restore default link and context if they exist. Otherwise clears.
   1594  hl_table[idx].sg_link = hl_table[idx].sg_deflink;
   1595  // Since we set the default link, set the location to where the default
   1596  // link was set.
   1597  hl_table[idx].sg_script_ctx = hl_table[idx].sg_deflink_sctx;
   1598 }
   1599 
   1600 /// \addtogroup LIST_XXX
   1601 /// @{
   1602 #define LIST_ATTR   1
   1603 #define LIST_STRING 2
   1604 #define LIST_INT    3
   1605 /// @}
   1606 
   1607 static void highlight_list_one(const int id)
   1608 {
   1609  const HlGroup *sgp = &hl_table[id - 1];  // index is ID minus one
   1610  bool didh = false;
   1611 
   1612  if (message_filtered(sgp->sg_name)) {
   1613    return;
   1614  }
   1615 
   1616  // don't list specialized groups if a parent is used instead
   1617  if (sgp->sg_parent && sgp->sg_cleared) {
   1618    return;
   1619  }
   1620 
   1621  didh = highlight_list_arg(id, didh, LIST_ATTR,
   1622                            sgp->sg_cterm, NULL, "cterm");
   1623  didh = highlight_list_arg(id, didh, LIST_INT,
   1624                            sgp->sg_cterm_fg, NULL, "ctermfg");
   1625  didh = highlight_list_arg(id, didh, LIST_INT,
   1626                            sgp->sg_cterm_bg, NULL, "ctermbg");
   1627 
   1628  didh = highlight_list_arg(id, didh, LIST_ATTR,
   1629                            sgp->sg_gui, NULL, "gui");
   1630  char hexbuf[8];
   1631  didh = highlight_list_arg(id, didh, LIST_STRING, 0,
   1632                            coloridx_to_name(sgp->sg_rgb_fg_idx, sgp->sg_rgb_fg, hexbuf), "guifg");
   1633  didh = highlight_list_arg(id, didh, LIST_STRING, 0,
   1634                            coloridx_to_name(sgp->sg_rgb_bg_idx, sgp->sg_rgb_bg, hexbuf), "guibg");
   1635  didh = highlight_list_arg(id, didh, LIST_STRING, 0,
   1636                            coloridx_to_name(sgp->sg_rgb_sp_idx, sgp->sg_rgb_sp, hexbuf), "guisp");
   1637 
   1638  didh = highlight_list_arg(id, didh, LIST_INT,
   1639                            sgp->sg_blend + 1, NULL, "blend");
   1640 
   1641  if (sgp->sg_link && !got_int) {
   1642    syn_list_header(didh, 0, id, true);
   1643    didh = true;
   1644    msg_puts_hl("links to", HLF_D, false);
   1645    msg_putchar(' ');
   1646    msg_outtrans(hl_table[hl_table[id - 1].sg_link - 1].sg_name, 0, false);
   1647  }
   1648 
   1649  if (!didh) {
   1650    highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", "");
   1651  }
   1652  if (p_verbose > 0) {
   1653    last_set_msg(sgp->sg_script_ctx);
   1654  }
   1655 }
   1656 
   1657 static bool hlgroup2dict(Dict *hl, NS ns_id, int hl_id, Arena *arena)
   1658 {
   1659  HlGroup *sgp = &hl_table[hl_id - 1];
   1660  int link = ns_id == 0 ? sgp->sg_link : ns_get_hl(&ns_id, hl_id, true, sgp->sg_set);
   1661  if (link == -1) {
   1662    return false;
   1663  }
   1664  if (ns_id == 0 && sgp->sg_cleared && sgp->sg_set == 0) {
   1665    // table entry was created but not ever set
   1666    return false;
   1667  }
   1668  HlAttrs attr =
   1669    syn_attr2entry(ns_id == 0 ? sgp->sg_attr : ns_get_hl(&ns_id, hl_id, false, sgp->sg_set));
   1670  *hl = arena_dict(arena, HLATTRS_DICT_SIZE + 1);
   1671  if (attr.rgb_ae_attr & HL_DEFAULT) {
   1672    PUT_C(*hl, "default", BOOLEAN_OBJ(true));
   1673  }
   1674  if (link > 0) {
   1675    assert(1 <= link && link <= highlight_ga.ga_len);
   1676    PUT_C(*hl, "link", CSTR_AS_OBJ(hl_table[link - 1].sg_name));
   1677  }
   1678  Dict hl_cterm = arena_dict(arena, HLATTRS_DICT_SIZE);
   1679  hlattrs2dict(hl, NULL, attr, true, true);
   1680  hlattrs2dict(hl, &hl_cterm, attr, false, true);
   1681  if (kv_size(hl_cterm)) {
   1682    PUT_C(*hl, "cterm", DICT_OBJ(hl_cterm));
   1683  }
   1684  return true;
   1685 }
   1686 
   1687 Dict ns_get_hl_defs(NS ns_id, Dict(get_highlight) *opts, Arena *arena, Error *err)
   1688 {
   1689  Boolean link = GET_BOOL_OR_TRUE(opts, get_highlight, link);
   1690  int id = -1;
   1691  if (HAS_KEY(opts, get_highlight, name)) {
   1692    Boolean create = GET_BOOL_OR_TRUE(opts, get_highlight, create);
   1693    id = create ? syn_check_group(opts->name.data, opts->name.size)
   1694                : syn_name2id_len(opts->name.data, opts->name.size);
   1695    if (id == 0 && !create) {
   1696      Dict attrs = ARRAY_DICT_INIT;
   1697      return attrs;
   1698    }
   1699  } else if (HAS_KEY(opts, get_highlight, id)) {
   1700    id = (int)opts->id;
   1701  }
   1702 
   1703  if (id != -1) {
   1704    VALIDATE(1 <= id && id <= highlight_ga.ga_len, "%s", "Highlight id out of bounds", {
   1705      goto cleanup;
   1706    });
   1707    Dict attrs = ARRAY_DICT_INIT;
   1708    hlgroup2dict(&attrs, ns_id, link ? id : syn_get_final_id(id), arena);
   1709    return attrs;
   1710  }
   1711  if (ERROR_SET(err)) {
   1712    goto cleanup;
   1713  }
   1714 
   1715  Dict rv = arena_dict(arena, (size_t)highlight_ga.ga_len);
   1716  for (int i = 1; i <= highlight_ga.ga_len; i++) {
   1717    Dict attrs = ARRAY_DICT_INIT;
   1718    if (!hlgroup2dict(&attrs, ns_id, i, arena)) {
   1719      continue;
   1720    }
   1721    PUT_C(rv, hl_table[(link ? i : syn_get_final_id(i)) - 1].sg_name, DICT_OBJ(attrs));
   1722  }
   1723 
   1724  return rv;
   1725 
   1726 cleanup:
   1727  return (Dict)ARRAY_DICT_INIT;
   1728 }
   1729 
   1730 /// Outputs a highlight when doing ":hi MyHighlight"
   1731 ///
   1732 /// @param type one of \ref LIST_XXX
   1733 /// @param iarg integer argument used if \p type == LIST_INT
   1734 /// @param sarg string used if \p type == LIST_STRING
   1735 static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, const char *sarg,
   1736                               const char *const name)
   1737 {
   1738  if (got_int) {
   1739    return false;
   1740  }
   1741 
   1742  if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0)) {
   1743    return didh;
   1744  }
   1745 
   1746  char buf[100];
   1747  const char *ts = buf;
   1748  if (type == LIST_INT) {
   1749    snprintf(buf, sizeof(buf), "%d", iarg - 1);
   1750  } else if (type == LIST_STRING) {
   1751    ts = sarg;
   1752  } else {    // type == LIST_ATTR
   1753    buf[0] = NUL;
   1754    for (int i = 0; hl_attr_table[i] != 0; i++) {
   1755      if (((hl_attr_table[i] & HL_UNDERLINE_MASK)
   1756           && ((iarg & HL_UNDERLINE_MASK) == hl_attr_table[i]))
   1757          || (!(hl_attr_table[i] & HL_UNDERLINE_MASK)
   1758              && (iarg & hl_attr_table[i]))) {
   1759        if (buf[0] != NUL) {
   1760          xstrlcat(buf, ",", 100);
   1761        }
   1762        xstrlcat(buf, hl_name_table[i], 100);
   1763        if (!(hl_attr_table[i] & HL_UNDERLINE_MASK)) {
   1764          iarg &= ~hl_attr_table[i];  // don't want "inverse"
   1765        }
   1766      }
   1767    }
   1768  }
   1769 
   1770  syn_list_header(didh, vim_strsize(ts) + (int)strlen(name) + 1, id, false);
   1771  didh = true;
   1772  if (!got_int) {
   1773    if (*name != NUL) {
   1774      msg_puts_hl(name, HLF_D, false);
   1775      msg_puts_hl("=", HLF_D, false);
   1776    }
   1777    msg_outtrans(ts, 0, false);
   1778  }
   1779  return didh;
   1780 }
   1781 
   1782 /// Check whether highlight group has attribute
   1783 ///
   1784 /// @param[in]  id  Highlight group to check.
   1785 /// @param[in]  flag  Attribute to check.
   1786 /// @param[in]  modec  'g' for GUI, 'c' for term.
   1787 ///
   1788 /// @return "1" if highlight group has attribute, NULL otherwise.
   1789 const char *highlight_has_attr(const int id, const int flag, const int modec)
   1790  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
   1791 {
   1792  if (id <= 0 || id > highlight_ga.ga_len) {
   1793    return NULL;
   1794  }
   1795 
   1796  int attr;
   1797 
   1798  if (modec == 'g') {
   1799    attr = hl_table[id - 1].sg_gui;
   1800  } else {
   1801    attr = hl_table[id - 1].sg_cterm;
   1802  }
   1803 
   1804  if (flag & HL_UNDERLINE_MASK) {
   1805    int ul = attr & HL_UNDERLINE_MASK;
   1806    return ul == flag ? "1" : NULL;
   1807  } else {
   1808    return (attr & flag) ? "1" : NULL;
   1809  }
   1810 }
   1811 
   1812 /// Return color name of the given highlight group
   1813 ///
   1814 /// @param[in]  id  Highlight group to work with.
   1815 /// @param[in]  what  What to return: one of "font", "fg", "bg", "sp", "fg#",
   1816 ///                   "bg#" or "sp#".
   1817 /// @param[in]  modec  'g' for GUI, 'c' for cterm and 't' for term.
   1818 ///
   1819 /// @return color name, possibly in a static buffer. Buffer will be overwritten
   1820 ///         on next highlight_color() call. May return NULL.
   1821 const char *highlight_color(const int id, const char *const what, const int modec)
   1822  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
   1823 {
   1824  static char name[20];
   1825  bool fg = false;
   1826  bool sp = false;
   1827  bool font = false;
   1828 
   1829  if (id <= 0 || id > highlight_ga.ga_len) {
   1830    return NULL;
   1831  }
   1832 
   1833  if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') {
   1834    fg = true;
   1835  } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
   1836             && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') {
   1837    font = true;
   1838  } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') {
   1839    sp = true;
   1840  } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) {
   1841    return NULL;
   1842  }
   1843 
   1844  int n;
   1845 
   1846  if (modec == 'g') {
   1847    if (what[2] == '#' && ui_rgb_attached()) {
   1848      if (fg) {
   1849        n = hl_table[id - 1].sg_rgb_fg;
   1850      } else if (sp) {
   1851        n = hl_table[id - 1].sg_rgb_sp;
   1852      } else {
   1853        n = hl_table[id - 1].sg_rgb_bg;
   1854      }
   1855      if (n < 0 || n > 0xffffff) {
   1856        return NULL;
   1857      }
   1858      snprintf(name, sizeof(name), "#%06x", n);
   1859      return name;
   1860    }
   1861    if (fg) {
   1862      return coloridx_to_name(hl_table[id - 1].sg_rgb_fg_idx, hl_table[id - 1].sg_rgb_fg, name);
   1863    } else if (sp) {
   1864      return coloridx_to_name(hl_table[id - 1].sg_rgb_sp_idx, hl_table[id - 1].sg_rgb_sp, name);
   1865    } else {
   1866      return coloridx_to_name(hl_table[id - 1].sg_rgb_bg_idx, hl_table[id - 1].sg_rgb_bg, name);
   1867    }
   1868  }
   1869  if (font || sp) {
   1870    return NULL;
   1871  }
   1872  if (modec == 'c') {
   1873    if (fg) {
   1874      n = hl_table[id - 1].sg_cterm_fg - 1;
   1875    } else {
   1876      n = hl_table[id - 1].sg_cterm_bg - 1;
   1877    }
   1878    if (n < 0) {
   1879      return NULL;
   1880    }
   1881    snprintf(name, sizeof(name), "%d", n);
   1882    return name;
   1883  }
   1884  // term doesn't have color.
   1885  return NULL;
   1886 }
   1887 
   1888 /// Output the syntax list header.
   1889 ///
   1890 /// @param did_header did header already
   1891 /// @param outlen length of string that comes
   1892 /// @param id highlight group id
   1893 /// @param force_newline always start a new line
   1894 /// @return true when started a new line.
   1895 bool syn_list_header(const bool did_header, const int outlen, const int id, bool force_newline)
   1896 {
   1897  int endcol = 19;
   1898  bool newline = true;
   1899  int name_col = 0;
   1900  bool adjust = true;
   1901 
   1902  if (!did_header) {
   1903    if (!ui_has(kUIMessages) || msg_col > 0) {
   1904      msg_putchar('\n');
   1905    }
   1906    if (got_int) {
   1907      return true;
   1908    }
   1909    msg_col = name_col = msg_outtrans(hl_table[id - 1].sg_name, 0, false);
   1910    endcol = 15;
   1911  } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) {
   1912    msg_putchar(' ');
   1913    adjust = false;
   1914  } else if (msg_col + outlen + 1 >= Columns || force_newline) {
   1915    msg_putchar('\n');
   1916    if (got_int) {
   1917      return true;
   1918    }
   1919  } else {
   1920    if (msg_col >= endcol) {    // wrap around is like starting a new line
   1921      newline = false;
   1922    }
   1923  }
   1924 
   1925  if (adjust) {
   1926    if (msg_col >= endcol) {
   1927      // output at least one space
   1928      endcol = msg_col + 1;
   1929    }
   1930 
   1931    msg_advance(endcol);
   1932  }
   1933 
   1934  // Show "xxx" with the attributes.
   1935  if (!did_header) {
   1936    if (endcol == Columns - 1 && endcol <= name_col) {
   1937      msg_putchar(' ');
   1938    }
   1939    msg_puts_hl("xxx", id, false);
   1940    msg_putchar(' ');
   1941  }
   1942 
   1943  return newline;
   1944 }
   1945 
   1946 /// Set the attribute numbers for a highlight group.
   1947 /// Called after one of the attributes has changed.
   1948 /// @param idx corrected highlight index
   1949 static void set_hl_attr(int idx)
   1950 {
   1951  HlAttrs at_en = HLATTRS_INIT;
   1952  HlGroup *sgp = hl_table + idx;
   1953 
   1954  at_en.cterm_ae_attr = (int32_t)sgp->sg_cterm;
   1955  at_en.cterm_fg_color = (int16_t)sgp->sg_cterm_fg;
   1956  at_en.cterm_bg_color = (int16_t)sgp->sg_cterm_bg;
   1957  at_en.rgb_ae_attr = (int32_t)sgp->sg_gui;
   1958  // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is
   1959  // initialized with 0 (by garray functions), check for sg_rgb_{f,b}g_name
   1960  // before setting attr_entry->{f,g}g_color to a other than -1
   1961  at_en.rgb_fg_color = sgp->sg_rgb_fg_idx != kColorIdxNone ? sgp->sg_rgb_fg : -1;
   1962  at_en.rgb_bg_color = sgp->sg_rgb_bg_idx != kColorIdxNone ? sgp->sg_rgb_bg : -1;
   1963  at_en.rgb_sp_color = sgp->sg_rgb_sp_idx != kColorIdxNone ? sgp->sg_rgb_sp : -1;
   1964  at_en.hl_blend = sgp->sg_blend;
   1965 
   1966  sgp->sg_attr = hl_get_syn_attr(0, idx + 1, at_en);
   1967 
   1968  // a cursor style uses this syn_id, make sure its attribute is updated.
   1969  if (cursor_mode_uses_syn_id(idx + 1)) {
   1970    ui_mode_info_set();
   1971  }
   1972 }
   1973 
   1974 int syn_name2id(const char *name)
   1975  FUNC_ATTR_NONNULL_ALL
   1976 {
   1977  if (name[0] == '@') {
   1978    // if we look up @aaa.bbb, we have to consider @aaa as well
   1979    return syn_check_group(name, strlen(name));
   1980  }
   1981  return syn_name2id_len(name, strlen(name));
   1982 }
   1983 
   1984 /// Lookup a highlight group name and return its ID.
   1985 ///
   1986 /// @param highlight name e.g. 'Cursor', 'Normal'
   1987 /// @return the highlight id, else 0 if \p name does not exist
   1988 int syn_name2id_len(const char *name, size_t len)
   1989  FUNC_ATTR_NONNULL_ALL
   1990 {
   1991  char name_u[MAX_SYN_NAME + 1];
   1992 
   1993  if (len == 0 || len > MAX_SYN_NAME) {
   1994    return 0;
   1995  }
   1996 
   1997  // Avoid using stricmp() too much, it's slow on some systems
   1998  // Avoid alloc()/free(), these are slow too.
   1999  vim_memcpy_up(name_u, name, len);
   2000  name_u[len] = NUL;
   2001 
   2002  // map_get(..., int) returns 0 when no key is present, which is
   2003  // the expected value for missing highlight group.
   2004  return map_get(cstr_t, int)(&highlight_unames, name_u);
   2005 }
   2006 
   2007 /// Lookup a highlight group name and return its attributes.
   2008 /// Return zero if not found.
   2009 int syn_name2attr(const char *name)
   2010  FUNC_ATTR_NONNULL_ALL
   2011 {
   2012  int id = syn_name2id(name);
   2013 
   2014  if (id != 0) {
   2015    return syn_id2attr(id);
   2016  }
   2017  return 0;
   2018 }
   2019 
   2020 /// Return true if highlight group "name" exists.
   2021 int highlight_exists(const char *name)
   2022 {
   2023  return syn_name2id(name) > 0;
   2024 }
   2025 
   2026 /// Return the name of highlight group "id".
   2027 /// When not a valid ID return an empty string.
   2028 char *syn_id2name(int id)
   2029 {
   2030  if (id <= 0 || id > highlight_ga.ga_len) {
   2031    return "";
   2032  }
   2033  return hl_table[id - 1].sg_name;
   2034 }
   2035 
   2036 /// Find highlight group name in the table and return its ID.
   2037 /// If it doesn't exist yet, a new entry is created.
   2038 ///
   2039 /// @param pp Highlight group name
   2040 /// @param len length of \p pp
   2041 ///
   2042 /// @return 0 for failure else the id of the group
   2043 int syn_check_group(const char *name, size_t len)
   2044 {
   2045  if (len > MAX_SYN_NAME) {
   2046    emsg(_(e_highlight_group_name_too_long));
   2047    return 0;
   2048  }
   2049  int id = syn_name2id_len(name, len);
   2050  if (id == 0) {  // doesn't exist yet
   2051    return syn_add_group(name, len);
   2052  }
   2053  return id;
   2054 }
   2055 
   2056 /// Add new highlight group and return its ID.
   2057 ///
   2058 /// @param name must be an allocated string, it will be consumed.
   2059 /// @return 0 for failure, else the allocated group id
   2060 /// @see syn_check_group
   2061 static int syn_add_group(const char *name, size_t len)
   2062 {
   2063  // Check that the name is valid (ASCII letters, digits, '_', '.', '@', '-').
   2064  for (size_t i = 0; i < len; i++) {
   2065    int c = (uint8_t)name[i];
   2066    if (!vim_isprintc(c)) {
   2067      emsg(_("E669: Unprintable character in group name"));
   2068      return 0;
   2069    } else if (!ASCII_ISALNUM(c) && c != '_' && c != '.' && c != '@' && c != '-') {
   2070      // '.' and '@' are allowed characters for use with treesitter capture names.
   2071      msg_source(HLF_W);
   2072      emsg(_(e_highlight_group_name_invalid_char));
   2073      return 0;
   2074    }
   2075  }
   2076 
   2077  int scoped_parent = 0;
   2078  if (len > 1 && name[0] == '@') {
   2079    char *delim = xmemrchr(name, '.', len);
   2080    if (delim) {
   2081      scoped_parent = syn_check_group(name, (size_t)(delim - name));
   2082    }
   2083  }
   2084 
   2085  // First call for this growarray: init growing array.
   2086  if (highlight_ga.ga_data == NULL) {
   2087    highlight_ga.ga_itemsize = sizeof(HlGroup);
   2088    ga_set_growsize(&highlight_ga, 10);
   2089    // 265 builtin groups, will always be used, plus some space
   2090    ga_grow(&highlight_ga, 300);
   2091  }
   2092 
   2093  if (highlight_ga.ga_len >= MAX_HL_ID) {
   2094    emsg(_("E849: Too many highlight and syntax groups"));
   2095    return 0;
   2096  }
   2097 
   2098  // Append another syntax_highlight entry.
   2099  HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga);
   2100  CLEAR_POINTER(hlgp);
   2101  hlgp->sg_name = arena_memdupz(&highlight_arena, name, len);
   2102  hlgp->sg_rgb_bg = -1;
   2103  hlgp->sg_rgb_fg = -1;
   2104  hlgp->sg_rgb_sp = -1;
   2105  hlgp->sg_rgb_bg_idx = kColorIdxNone;
   2106  hlgp->sg_rgb_fg_idx = kColorIdxNone;
   2107  hlgp->sg_rgb_sp_idx = kColorIdxNone;
   2108  hlgp->sg_blend = -1;
   2109  hlgp->sg_name_u = arena_memdupz(&highlight_arena, name, len);
   2110  hlgp->sg_parent = scoped_parent;
   2111  // will get set to false by caller if settings are added
   2112  hlgp->sg_cleared = true;
   2113  vim_strup(hlgp->sg_name_u);
   2114 
   2115  int id = highlight_ga.ga_len;  // ID is index plus one
   2116 
   2117  map_put(cstr_t, int)(&highlight_unames, hlgp->sg_name_u, id);
   2118 
   2119  return id;
   2120 }
   2121 
   2122 /// Translate a group ID to highlight attributes.
   2123 /// @see syn_attr2entry
   2124 int syn_id2attr(int hl_id)
   2125 {
   2126  bool optional = false;
   2127  return syn_ns_id2attr(-1, hl_id, &optional);
   2128 }
   2129 
   2130 int syn_ns_id2attr(int ns_id, int hl_id, bool *optional)
   2131  FUNC_ATTR_NONNULL_ALL
   2132 {
   2133  if (syn_ns_get_final_id(&ns_id, &hl_id)) {
   2134    // If the namespace explicitly defines a group to be empty, it is not optional
   2135    *optional = false;
   2136  }
   2137  HlGroup *sgp = &hl_table[hl_id - 1];  // index is ID minus one
   2138 
   2139  int attr = ns_get_hl(&ns_id, hl_id, false, sgp->sg_set);
   2140 
   2141  // if a highlight group is optional, don't use the global value
   2142  if (attr >= 0 || (*optional && ns_id > 0)) {
   2143    return attr;
   2144  }
   2145  return sgp->sg_attr;
   2146 }
   2147 
   2148 /// Translate a group ID to the final group ID (following links).
   2149 int syn_get_final_id(int hl_id)
   2150 {
   2151  int ns_id = curwin->w_ns_hl_active;
   2152  syn_ns_get_final_id(&ns_id, &hl_id);
   2153  return hl_id;
   2154 }
   2155 
   2156 bool syn_ns_get_final_id(int *ns_id, int *hl_idp)
   2157 {
   2158  int hl_id = *hl_idp;
   2159  bool used = false;
   2160 
   2161  if (hl_id > highlight_ga.ga_len || hl_id < 1) {
   2162    *hl_idp = 0;
   2163    return false;  // Can be called from eval!!
   2164  }
   2165 
   2166  // Follow links until there is no more.
   2167  // Look out for loops!  Break after 100 links.
   2168  for (int count = 100; --count >= 0;) {
   2169    HlGroup *sgp = &hl_table[hl_id - 1];  // index is ID minus one
   2170 
   2171    // TODO(bfredl): when using "tmp" attribute (no link) the function might be
   2172    // called twice. it needs be smart enough to remember attr only to
   2173    // syn_id2attr time
   2174    int check = ns_get_hl(ns_id, hl_id, true, sgp->sg_set);
   2175    if (check == 0) {
   2176      *hl_idp = hl_id;
   2177      return true;  // how dare! it broke the link!
   2178    } else if (check > 0) {
   2179      used = true;
   2180      hl_id = check;
   2181      continue;
   2182    }
   2183 
   2184    if (sgp->sg_link > 0 && sgp->sg_link <= highlight_ga.ga_len) {
   2185      hl_id = sgp->sg_link;
   2186    } else if (sgp->sg_cleared && sgp->sg_parent > 0) {
   2187      hl_id = sgp->sg_parent;
   2188    } else {
   2189      break;
   2190    }
   2191  }
   2192 
   2193  *hl_idp = hl_id;
   2194  return used;
   2195 }
   2196 
   2197 /// Refresh the color attributes of all highlight groups.
   2198 void highlight_attr_set_all(void)
   2199 {
   2200  for (int idx = 0; idx < highlight_ga.ga_len; idx++) {
   2201    HlGroup *sgp = &hl_table[idx];
   2202    if (sgp->sg_rgb_bg_idx == kColorIdxFg) {
   2203      sgp->sg_rgb_bg = normal_fg;
   2204    } else if (sgp->sg_rgb_bg_idx == kColorIdxBg) {
   2205      sgp->sg_rgb_bg = normal_bg;
   2206    }
   2207    if (sgp->sg_rgb_fg_idx == kColorIdxFg) {
   2208      sgp->sg_rgb_fg = normal_fg;
   2209    } else if (sgp->sg_rgb_fg_idx == kColorIdxBg) {
   2210      sgp->sg_rgb_fg = normal_bg;
   2211    }
   2212    if (sgp->sg_rgb_sp_idx == kColorIdxFg) {
   2213      sgp->sg_rgb_sp = normal_fg;
   2214    } else if (sgp->sg_rgb_sp_idx == kColorIdxBg) {
   2215      sgp->sg_rgb_sp = normal_bg;
   2216    }
   2217    set_hl_attr(idx);
   2218  }
   2219 }
   2220 
   2221 // Apply difference between User[1-9] and HLF_S to HLF_SNC.
   2222 static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table)
   2223  FUNC_ATTR_NONNULL_ALL
   2224 {
   2225  HlGroup *const hlt = hl_table;
   2226 
   2227  if (id_alt == 0) {
   2228    CLEAR_POINTER(&hlt[hlcnt + i]);
   2229    hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
   2230    hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
   2231  } else {
   2232    memmove(&hlt[hlcnt + i], &hlt[id_alt - 1], sizeof(HlGroup));
   2233  }
   2234  hlt[hlcnt + i].sg_link = 0;
   2235 
   2236  hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
   2237  if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) {
   2238    hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
   2239  }
   2240  if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) {
   2241    hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
   2242  }
   2243  hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
   2244  if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) {
   2245    hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg;
   2246  }
   2247  if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) {
   2248    hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg;
   2249  }
   2250  if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) {
   2251    hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp;
   2252  }
   2253  highlight_ga.ga_len = hlcnt + i + 1;
   2254  set_hl_attr(hlcnt + i);  // At long last we can apply
   2255  table[i] = syn_id2attr(hlcnt + i + 1);
   2256 }
   2257 
   2258 /// Translate highlight groups into attributes in highlight_attr[] and set up
   2259 /// the user highlights User1..9. A set of corresponding highlights to use on
   2260 /// top of HLF_SNC is computed.  Called only when nvim starts and upon first
   2261 /// screen redraw after any :highlight command.
   2262 void highlight_changed(void)
   2263 {
   2264  char userhl[30];  // use 30 to avoid compiler warning
   2265  int id_S = -1;
   2266  int id_SNC = 0;
   2267 
   2268  need_highlight_changed = false;
   2269 
   2270  // sentinel value. used when no highlight is active
   2271  highlight_attr[HLF_NONE] = 0;
   2272 
   2273  /// Translate builtin highlight groups into attributes for quick lookup.
   2274  for (int hlf = 1; hlf < HLF_COUNT; hlf++) {
   2275    int id = syn_check_group(hlf_names[hlf], strlen(hlf_names[hlf]));
   2276    if (id == 0) {
   2277      abort();
   2278    }
   2279    int ns_id = -1;
   2280    int final_id = id;
   2281    syn_ns_get_final_id(&ns_id, &final_id);
   2282    if (hlf == HLF_SNC) {
   2283      id_SNC = final_id;
   2284    } else if (hlf == HLF_S) {
   2285      id_S = final_id;
   2286    }
   2287 
   2288    highlight_attr[hlf] = hl_get_ui_attr(ns_id, hlf, final_id, hlf == HLF_INACTIVE);
   2289 
   2290    if (highlight_attr[hlf] != highlight_attr_last[hlf]) {
   2291      if (hlf == HLF_MSG) {
   2292        clear_cmdline = true;
   2293        HlAttrs attrs = syn_attr2entry(highlight_attr[hlf]);
   2294        msg_grid.blending = attrs.hl_blend > -1;
   2295      }
   2296      ui_call_hl_group_set(cstr_as_string(hlf_names[hlf]),
   2297                           highlight_attr[hlf]);
   2298      highlight_attr_last[hlf] = highlight_attr[hlf];
   2299    }
   2300  }
   2301 
   2302  // Setup the user highlights
   2303  //
   2304  // Temporarily utilize 10 more hl entries:
   2305  // 9 for User1-User9 combined with StatusLineNC
   2306  // 1 for StatusLine default
   2307  // Must to be in there simultaneously in case of table overflows in
   2308  // get_attr_entry()
   2309  ga_grow(&highlight_ga, 10);
   2310  int hlcnt = highlight_ga.ga_len;
   2311  if (id_S == -1) {
   2312    // Make sure id_S is always valid to simplify code below. Use the last entry
   2313    CLEAR_POINTER(&hl_table[hlcnt + 9]);
   2314    id_S = hlcnt + 10;
   2315  }
   2316  for (int i = 0; i < 9; i++) {
   2317    snprintf(userhl, sizeof(userhl), "User%d", i + 1);
   2318    int id = syn_name2id(userhl);
   2319    if (id == 0) {
   2320      highlight_user[i] = 0;
   2321      highlight_stlnc[i] = 0;
   2322    } else {
   2323      highlight_user[i] = syn_id2attr(id);
   2324      combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, HLF_SNC, highlight_stlnc);
   2325    }
   2326  }
   2327  highlight_ga.ga_len = hlcnt;
   2328 
   2329  decor_provider_invalidate_hl();
   2330 }
   2331 
   2332 /// Handle command line completion for :highlight command.
   2333 void set_context_in_highlight_cmd(expand_T *xp, const char *arg)
   2334 {
   2335  // Default: expand group names.
   2336  xp->xp_context = EXPAND_HIGHLIGHT;
   2337  xp->xp_pattern = (char *)arg;
   2338  include_link = 2;
   2339  include_default = 1;
   2340 
   2341  if (*arg == NUL) {
   2342    return;
   2343  }
   2344 
   2345  // (part of) subcommand already typed
   2346  const char *p = skiptowhite(arg);
   2347  if (*p == NUL) {
   2348    return;
   2349  }
   2350 
   2351  // past "default" or group name
   2352  include_default = 0;
   2353  if (strncmp("default", arg, (unsigned)(p - arg)) == 0) {
   2354    arg = skipwhite(p);
   2355    xp->xp_pattern = (char *)arg;
   2356    p = skiptowhite(arg);
   2357  }
   2358  if (*p == NUL) {
   2359    return;
   2360  }
   2361 
   2362  // past group name
   2363  include_link = 0;
   2364  if (arg[1] == 'i' && arg[0] == 'N') {
   2365    highlight_list();
   2366  }
   2367  if (strncmp("link", arg, (unsigned)(p - arg)) == 0
   2368      || strncmp("clear", arg, (unsigned)(p - arg)) == 0) {
   2369    xp->xp_pattern = skipwhite(p);
   2370    p = skiptowhite(xp->xp_pattern);
   2371    if (*p != NUL) {  // past first group name
   2372      xp->xp_pattern = skipwhite(p);
   2373      p = skiptowhite(xp->xp_pattern);
   2374    }
   2375  }
   2376  if (*p != NUL) {  // past group name(s)
   2377    xp->xp_context = EXPAND_NOTHING;
   2378  }
   2379 }
   2380 
   2381 /// List highlighting matches in a nice way.
   2382 static void highlight_list(void)
   2383 {
   2384  for (int i = 10; --i >= 0;) {
   2385    highlight_list_two(i, HLF_D);
   2386  }
   2387  for (int i = 40; --i >= 0;) {
   2388    highlight_list_two(99, 0);
   2389  }
   2390 }
   2391 
   2392 static void highlight_list_two(int cnt, int id)
   2393 {
   2394  msg_puts_hl(&("N \bI \b!  \b"[cnt / 11]), id, false);
   2395  msg_clr_eos();
   2396  ui_flush();
   2397  // TODO(justinmk): is this delay needed? ":hi" seems to work without it.
   2398  os_delay(cnt == 99 ? 40 : (uint64_t)cnt * 50, false);
   2399 }
   2400 
   2401 /// Function given to ExpandGeneric() to obtain the list of group names.
   2402 char *get_highlight_name(expand_T *const xp, int idx)
   2403  FUNC_ATTR_WARN_UNUSED_RESULT
   2404 {
   2405  return (char *)get_highlight_name_ext(xp, idx, true);
   2406 }
   2407 
   2408 /// Obtain a highlight group name.
   2409 ///
   2410 /// @param skip_cleared  if true don't return a cleared entry.
   2411 const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared)
   2412  FUNC_ATTR_WARN_UNUSED_RESULT
   2413 {
   2414  if (idx < 0) {
   2415    return NULL;
   2416  }
   2417 
   2418  // Items are never removed from the table, skip the ones that were cleared.
   2419  if (skip_cleared && idx < highlight_ga.ga_len && hl_table[idx].sg_cleared) {
   2420    return "";
   2421  }
   2422 
   2423  if (idx == highlight_ga.ga_len && include_none != 0) {
   2424    return "none";
   2425  } else if (idx == highlight_ga.ga_len + include_none
   2426             && include_default != 0) {
   2427    return "default";
   2428  } else if (idx == highlight_ga.ga_len + include_none + include_default
   2429             && include_link != 0) {
   2430    return "link";
   2431  } else if (idx == highlight_ga.ga_len + include_none + include_default + 1
   2432             && include_link != 0) {
   2433    return "clear";
   2434  } else if (idx >= highlight_ga.ga_len) {
   2435    return NULL;
   2436  }
   2437  return hl_table[idx].sg_name;
   2438 }
   2439 
   2440 color_name_table_T color_name_table[] = {
   2441  // Colors from rgb.txt
   2442  { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) },
   2443  { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) },
   2444  { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) },
   2445  { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) },
   2446  { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) },
   2447  { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) },
   2448  { "Aqua", RGB_(0x00, 0xff, 0xff) },
   2449  { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) },
   2450  { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) },
   2451  { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) },
   2452  { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) },
   2453  { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) },
   2454  { "Azure", RGB_(0xf0, 0xff, 0xff) },
   2455  { "Azure1", RGB_(0xf0, 0xff, 0xff) },
   2456  { "Azure2", RGB_(0xe0, 0xee, 0xee) },
   2457  { "Azure3", RGB_(0xc1, 0xcd, 0xcd) },
   2458  { "Azure4", RGB_(0x83, 0x8b, 0x8b) },
   2459  { "Beige", RGB_(0xf5, 0xf5, 0xdc) },
   2460  { "Bisque", RGB_(0xff, 0xe4, 0xc4) },
   2461  { "Bisque1", RGB_(0xff, 0xe4, 0xc4) },
   2462  { "Bisque2", RGB_(0xee, 0xd5, 0xb7) },
   2463  { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) },
   2464  { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) },
   2465  { "Black", RGB_(0x00, 0x00, 0x00) },
   2466  { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) },
   2467  { "Blue", RGB_(0x00, 0x00, 0xff) },
   2468  { "Blue1", RGB_(0x0, 0x0, 0xff) },
   2469  { "Blue2", RGB_(0x0, 0x0, 0xee) },
   2470  { "Blue3", RGB_(0x0, 0x0, 0xcd) },
   2471  { "Blue4", RGB_(0x0, 0x0, 0x8b) },
   2472  { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) },
   2473  { "Brown", RGB_(0xa5, 0x2a, 0x2a) },
   2474  { "Brown1", RGB_(0xff, 0x40, 0x40) },
   2475  { "Brown2", RGB_(0xee, 0x3b, 0x3b) },
   2476  { "Brown3", RGB_(0xcd, 0x33, 0x33) },
   2477  { "Brown4", RGB_(0x8b, 0x23, 0x23) },
   2478  { "BurlyWood", RGB_(0xde, 0xb8, 0x87) },
   2479  { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) },
   2480  { "Burlywood2", RGB_(0xee, 0xc5, 0x91) },
   2481  { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) },
   2482  { "Burlywood4", RGB_(0x8b, 0x73, 0x55) },
   2483  { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) },
   2484  { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) },
   2485  { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) },
   2486  { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) },
   2487  { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) },
   2488  { "ChartReuse", RGB_(0x7f, 0xff, 0x00) },
   2489  { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) },
   2490  { "Chartreuse2", RGB_(0x76, 0xee, 0x0) },
   2491  { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) },
   2492  { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) },
   2493  { "Chocolate", RGB_(0xd2, 0x69, 0x1e) },
   2494  { "Chocolate1", RGB_(0xff, 0x7f, 0x24) },
   2495  { "Chocolate2", RGB_(0xee, 0x76, 0x21) },
   2496  { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) },
   2497  { "Chocolate4", RGB_(0x8b, 0x45, 0x13) },
   2498  { "Coral", RGB_(0xff, 0x7f, 0x50) },
   2499  { "Coral1", RGB_(0xff, 0x72, 0x56) },
   2500  { "Coral2", RGB_(0xee, 0x6a, 0x50) },
   2501  { "Coral3", RGB_(0xcd, 0x5b, 0x45) },
   2502  { "Coral4", RGB_(0x8b, 0x3e, 0x2f) },
   2503  { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) },
   2504  { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) },
   2505  { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) },
   2506  { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) },
   2507  { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) },
   2508  { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) },
   2509  { "Crimson", RGB_(0xdc, 0x14, 0x3c) },
   2510  { "Cyan", RGB_(0x00, 0xff, 0xff) },
   2511  { "Cyan1", RGB_(0x0, 0xff, 0xff) },
   2512  { "Cyan2", RGB_(0x0, 0xee, 0xee) },
   2513  { "Cyan3", RGB_(0x0, 0xcd, 0xcd) },
   2514  { "Cyan4", RGB_(0x0, 0x8b, 0x8b) },
   2515  { "DarkBlue", RGB_(0x00, 0x00, 0x8b) },
   2516  { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) },
   2517  { "DarkGoldenrod", RGB_(0xb8, 0x86, 0x0b) },
   2518  { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) },
   2519  { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) },
   2520  { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) },
   2521  { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) },
   2522  { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) },
   2523  { "DarkGreen", RGB_(0x00, 0x64, 0x00) },
   2524  { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) },
   2525  { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) },
   2526  { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) },
   2527  { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) },
   2528  { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) },
   2529  { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) },
   2530  { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) },
   2531  { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) },
   2532  { "DarkOrange", RGB_(0xff, 0x8c, 0x00) },
   2533  { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) },
   2534  { "DarkOrange2", RGB_(0xee, 0x76, 0x0) },
   2535  { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) },
   2536  { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) },
   2537  { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) },
   2538  { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) },
   2539  { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) },
   2540  { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) },
   2541  { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) },
   2542  { "DarkRed", RGB_(0x8b, 0x00, 0x00) },
   2543  { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) },
   2544  { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) },
   2545  { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) },
   2546  { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) },
   2547  { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) },
   2548  { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) },
   2549  { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) },
   2550  { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) },
   2551  { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) },
   2552  { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) },
   2553  { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) },
   2554  { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) },
   2555  { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) },
   2556  { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) },
   2557  { "DarkViolet", RGB_(0x94, 0x00, 0xd3) },
   2558  { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) },
   2559  { "DeepPink", RGB_(0xff, 0x14, 0x93) },
   2560  { "DeepPink1", RGB_(0xff, 0x14, 0x93) },
   2561  { "DeepPink2", RGB_(0xee, 0x12, 0x89) },
   2562  { "DeepPink3", RGB_(0xcd, 0x10, 0x76) },
   2563  { "DeepPink4", RGB_(0x8b, 0xa, 0x50) },
   2564  { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) },
   2565  { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) },
   2566  { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) },
   2567  { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) },
   2568  { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) },
   2569  { "DimGray", RGB_(0x69, 0x69, 0x69) },
   2570  { "DimGrey", RGB_(0x69, 0x69, 0x69) },
   2571  { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) },
   2572  { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) },
   2573  { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) },
   2574  { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) },
   2575  { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) },
   2576  { "Firebrick", RGB_(0xb2, 0x22, 0x22) },
   2577  { "Firebrick1", RGB_(0xff, 0x30, 0x30) },
   2578  { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) },
   2579  { "Firebrick3", RGB_(0xcd, 0x26, 0x26) },
   2580  { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) },
   2581  { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) },
   2582  { "ForestGreen", RGB_(0x22, 0x8b, 0x22) },
   2583  { "Fuchsia", RGB_(0xff, 0x00, 0xff) },
   2584  { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) },
   2585  { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) },
   2586  { "Gold", RGB_(0xff, 0xd7, 0x00) },
   2587  { "Gold1", RGB_(0xff, 0xd7, 0x0) },
   2588  { "Gold2", RGB_(0xee, 0xc9, 0x0) },
   2589  { "Gold3", RGB_(0xcd, 0xad, 0x0) },
   2590  { "Gold4", RGB_(0x8b, 0x75, 0x0) },
   2591  { "Goldenrod", RGB_(0xda, 0xa5, 0x20) },
   2592  { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) },
   2593  { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) },
   2594  { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) },
   2595  { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) },
   2596  { "Gray", RGB_(0x80, 0x80, 0x80) },
   2597  { "Gray0", RGB_(0x0, 0x0, 0x0) },
   2598  { "Gray1", RGB_(0x3, 0x3, 0x3) },
   2599  { "Gray10", RGB_(0x1a, 0x1a, 0x1a) },
   2600  { "Gray100", RGB_(0xff, 0xff, 0xff) },
   2601  { "Gray11", RGB_(0x1c, 0x1c, 0x1c) },
   2602  { "Gray12", RGB_(0x1f, 0x1f, 0x1f) },
   2603  { "Gray13", RGB_(0x21, 0x21, 0x21) },
   2604  { "Gray14", RGB_(0x24, 0x24, 0x24) },
   2605  { "Gray15", RGB_(0x26, 0x26, 0x26) },
   2606  { "Gray16", RGB_(0x29, 0x29, 0x29) },
   2607  { "Gray17", RGB_(0x2b, 0x2b, 0x2b) },
   2608  { "Gray18", RGB_(0x2e, 0x2e, 0x2e) },
   2609  { "Gray19", RGB_(0x30, 0x30, 0x30) },
   2610  { "Gray2", RGB_(0x5, 0x5, 0x5) },
   2611  { "Gray20", RGB_(0x33, 0x33, 0x33) },
   2612  { "Gray21", RGB_(0x36, 0x36, 0x36) },
   2613  { "Gray22", RGB_(0x38, 0x38, 0x38) },
   2614  { "Gray23", RGB_(0x3b, 0x3b, 0x3b) },
   2615  { "Gray24", RGB_(0x3d, 0x3d, 0x3d) },
   2616  { "Gray25", RGB_(0x40, 0x40, 0x40) },
   2617  { "Gray26", RGB_(0x42, 0x42, 0x42) },
   2618  { "Gray27", RGB_(0x45, 0x45, 0x45) },
   2619  { "Gray28", RGB_(0x47, 0x47, 0x47) },
   2620  { "Gray29", RGB_(0x4a, 0x4a, 0x4a) },
   2621  { "Gray3", RGB_(0x8, 0x8, 0x8) },
   2622  { "Gray30", RGB_(0x4d, 0x4d, 0x4d) },
   2623  { "Gray31", RGB_(0x4f, 0x4f, 0x4f) },
   2624  { "Gray32", RGB_(0x52, 0x52, 0x52) },
   2625  { "Gray33", RGB_(0x54, 0x54, 0x54) },
   2626  { "Gray34", RGB_(0x57, 0x57, 0x57) },
   2627  { "Gray35", RGB_(0x59, 0x59, 0x59) },
   2628  { "Gray36", RGB_(0x5c, 0x5c, 0x5c) },
   2629  { "Gray37", RGB_(0x5e, 0x5e, 0x5e) },
   2630  { "Gray38", RGB_(0x61, 0x61, 0x61) },
   2631  { "Gray39", RGB_(0x63, 0x63, 0x63) },
   2632  { "Gray4", RGB_(0xa, 0xa, 0xa) },
   2633  { "Gray40", RGB_(0x66, 0x66, 0x66) },
   2634  { "Gray41", RGB_(0x69, 0x69, 0x69) },
   2635  { "Gray42", RGB_(0x6b, 0x6b, 0x6b) },
   2636  { "Gray43", RGB_(0x6e, 0x6e, 0x6e) },
   2637  { "Gray44", RGB_(0x70, 0x70, 0x70) },
   2638  { "Gray45", RGB_(0x73, 0x73, 0x73) },
   2639  { "Gray46", RGB_(0x75, 0x75, 0x75) },
   2640  { "Gray47", RGB_(0x78, 0x78, 0x78) },
   2641  { "Gray48", RGB_(0x7a, 0x7a, 0x7a) },
   2642  { "Gray49", RGB_(0x7d, 0x7d, 0x7d) },
   2643  { "Gray5", RGB_(0xd, 0xd, 0xd) },
   2644  { "Gray50", RGB_(0x7f, 0x7f, 0x7f) },
   2645  { "Gray51", RGB_(0x82, 0x82, 0x82) },
   2646  { "Gray52", RGB_(0x85, 0x85, 0x85) },
   2647  { "Gray53", RGB_(0x87, 0x87, 0x87) },
   2648  { "Gray54", RGB_(0x8a, 0x8a, 0x8a) },
   2649  { "Gray55", RGB_(0x8c, 0x8c, 0x8c) },
   2650  { "Gray56", RGB_(0x8f, 0x8f, 0x8f) },
   2651  { "Gray57", RGB_(0x91, 0x91, 0x91) },
   2652  { "Gray58", RGB_(0x94, 0x94, 0x94) },
   2653  { "Gray59", RGB_(0x96, 0x96, 0x96) },
   2654  { "Gray6", RGB_(0xf, 0xf, 0xf) },
   2655  { "Gray60", RGB_(0x99, 0x99, 0x99) },
   2656  { "Gray61", RGB_(0x9c, 0x9c, 0x9c) },
   2657  { "Gray62", RGB_(0x9e, 0x9e, 0x9e) },
   2658  { "Gray63", RGB_(0xa1, 0xa1, 0xa1) },
   2659  { "Gray64", RGB_(0xa3, 0xa3, 0xa3) },
   2660  { "Gray65", RGB_(0xa6, 0xa6, 0xa6) },
   2661  { "Gray66", RGB_(0xa8, 0xa8, 0xa8) },
   2662  { "Gray67", RGB_(0xab, 0xab, 0xab) },
   2663  { "Gray68", RGB_(0xad, 0xad, 0xad) },
   2664  { "Gray69", RGB_(0xb0, 0xb0, 0xb0) },
   2665  { "Gray7", RGB_(0x12, 0x12, 0x12) },
   2666  { "Gray70", RGB_(0xb3, 0xb3, 0xb3) },
   2667  { "Gray71", RGB_(0xb5, 0xb5, 0xb5) },
   2668  { "Gray72", RGB_(0xb8, 0xb8, 0xb8) },
   2669  { "Gray73", RGB_(0xba, 0xba, 0xba) },
   2670  { "Gray74", RGB_(0xbd, 0xbd, 0xbd) },
   2671  { "Gray75", RGB_(0xbf, 0xbf, 0xbf) },
   2672  { "Gray76", RGB_(0xc2, 0xc2, 0xc2) },
   2673  { "Gray77", RGB_(0xc4, 0xc4, 0xc4) },
   2674  { "Gray78", RGB_(0xc7, 0xc7, 0xc7) },
   2675  { "Gray79", RGB_(0xc9, 0xc9, 0xc9) },
   2676  { "Gray8", RGB_(0x14, 0x14, 0x14) },
   2677  { "Gray80", RGB_(0xcc, 0xcc, 0xcc) },
   2678  { "Gray81", RGB_(0xcf, 0xcf, 0xcf) },
   2679  { "Gray82", RGB_(0xd1, 0xd1, 0xd1) },
   2680  { "Gray83", RGB_(0xd4, 0xd4, 0xd4) },
   2681  { "Gray84", RGB_(0xd6, 0xd6, 0xd6) },
   2682  { "Gray85", RGB_(0xd9, 0xd9, 0xd9) },
   2683  { "Gray86", RGB_(0xdb, 0xdb, 0xdb) },
   2684  { "Gray87", RGB_(0xde, 0xde, 0xde) },
   2685  { "Gray88", RGB_(0xe0, 0xe0, 0xe0) },
   2686  { "Gray89", RGB_(0xe3, 0xe3, 0xe3) },
   2687  { "Gray9", RGB_(0x17, 0x17, 0x17) },
   2688  { "Gray90", RGB_(0xe5, 0xe5, 0xe5) },
   2689  { "Gray91", RGB_(0xe8, 0xe8, 0xe8) },
   2690  { "Gray92", RGB_(0xeb, 0xeb, 0xeb) },
   2691  { "Gray93", RGB_(0xed, 0xed, 0xed) },
   2692  { "Gray94", RGB_(0xf0, 0xf0, 0xf0) },
   2693  { "Gray95", RGB_(0xf2, 0xf2, 0xf2) },
   2694  { "Gray96", RGB_(0xf5, 0xf5, 0xf5) },
   2695  { "Gray97", RGB_(0xf7, 0xf7, 0xf7) },
   2696  { "Gray98", RGB_(0xfa, 0xfa, 0xfa) },
   2697  { "Gray99", RGB_(0xfc, 0xfc, 0xfc) },
   2698  { "Green", RGB_(0x00, 0x80, 0x00) },
   2699  { "Green1", RGB_(0x0, 0xff, 0x0) },
   2700  { "Green2", RGB_(0x0, 0xee, 0x0) },
   2701  { "Green3", RGB_(0x0, 0xcd, 0x0) },
   2702  { "Green4", RGB_(0x0, 0x8b, 0x0) },
   2703  { "GreenYellow", RGB_(0xad, 0xff, 0x2f) },
   2704  { "Grey", RGB_(0x80, 0x80, 0x80) },
   2705  { "Grey0", RGB_(0x0, 0x0, 0x0) },
   2706  { "Grey1", RGB_(0x3, 0x3, 0x3) },
   2707  { "Grey10", RGB_(0x1a, 0x1a, 0x1a) },
   2708  { "Grey100", RGB_(0xff, 0xff, 0xff) },
   2709  { "Grey11", RGB_(0x1c, 0x1c, 0x1c) },
   2710  { "Grey12", RGB_(0x1f, 0x1f, 0x1f) },
   2711  { "Grey13", RGB_(0x21, 0x21, 0x21) },
   2712  { "Grey14", RGB_(0x24, 0x24, 0x24) },
   2713  { "Grey15", RGB_(0x26, 0x26, 0x26) },
   2714  { "Grey16", RGB_(0x29, 0x29, 0x29) },
   2715  { "Grey17", RGB_(0x2b, 0x2b, 0x2b) },
   2716  { "Grey18", RGB_(0x2e, 0x2e, 0x2e) },
   2717  { "Grey19", RGB_(0x30, 0x30, 0x30) },
   2718  { "Grey2", RGB_(0x5, 0x5, 0x5) },
   2719  { "Grey20", RGB_(0x33, 0x33, 0x33) },
   2720  { "Grey21", RGB_(0x36, 0x36, 0x36) },
   2721  { "Grey22", RGB_(0x38, 0x38, 0x38) },
   2722  { "Grey23", RGB_(0x3b, 0x3b, 0x3b) },
   2723  { "Grey24", RGB_(0x3d, 0x3d, 0x3d) },
   2724  { "Grey25", RGB_(0x40, 0x40, 0x40) },
   2725  { "Grey26", RGB_(0x42, 0x42, 0x42) },
   2726  { "Grey27", RGB_(0x45, 0x45, 0x45) },
   2727  { "Grey28", RGB_(0x47, 0x47, 0x47) },
   2728  { "Grey29", RGB_(0x4a, 0x4a, 0x4a) },
   2729  { "Grey3", RGB_(0x8, 0x8, 0x8) },
   2730  { "Grey30", RGB_(0x4d, 0x4d, 0x4d) },
   2731  { "Grey31", RGB_(0x4f, 0x4f, 0x4f) },
   2732  { "Grey32", RGB_(0x52, 0x52, 0x52) },
   2733  { "Grey33", RGB_(0x54, 0x54, 0x54) },
   2734  { "Grey34", RGB_(0x57, 0x57, 0x57) },
   2735  { "Grey35", RGB_(0x59, 0x59, 0x59) },
   2736  { "Grey36", RGB_(0x5c, 0x5c, 0x5c) },
   2737  { "Grey37", RGB_(0x5e, 0x5e, 0x5e) },
   2738  { "Grey38", RGB_(0x61, 0x61, 0x61) },
   2739  { "Grey39", RGB_(0x63, 0x63, 0x63) },
   2740  { "Grey4", RGB_(0xa, 0xa, 0xa) },
   2741  { "Grey40", RGB_(0x66, 0x66, 0x66) },
   2742  { "Grey41", RGB_(0x69, 0x69, 0x69) },
   2743  { "Grey42", RGB_(0x6b, 0x6b, 0x6b) },
   2744  { "Grey43", RGB_(0x6e, 0x6e, 0x6e) },
   2745  { "Grey44", RGB_(0x70, 0x70, 0x70) },
   2746  { "Grey45", RGB_(0x73, 0x73, 0x73) },
   2747  { "Grey46", RGB_(0x75, 0x75, 0x75) },
   2748  { "Grey47", RGB_(0x78, 0x78, 0x78) },
   2749  { "Grey48", RGB_(0x7a, 0x7a, 0x7a) },
   2750  { "Grey49", RGB_(0x7d, 0x7d, 0x7d) },
   2751  { "Grey5", RGB_(0xd, 0xd, 0xd) },
   2752  { "Grey50", RGB_(0x7f, 0x7f, 0x7f) },
   2753  { "Grey51", RGB_(0x82, 0x82, 0x82) },
   2754  { "Grey52", RGB_(0x85, 0x85, 0x85) },
   2755  { "Grey53", RGB_(0x87, 0x87, 0x87) },
   2756  { "Grey54", RGB_(0x8a, 0x8a, 0x8a) },
   2757  { "Grey55", RGB_(0x8c, 0x8c, 0x8c) },
   2758  { "Grey56", RGB_(0x8f, 0x8f, 0x8f) },
   2759  { "Grey57", RGB_(0x91, 0x91, 0x91) },
   2760  { "Grey58", RGB_(0x94, 0x94, 0x94) },
   2761  { "Grey59", RGB_(0x96, 0x96, 0x96) },
   2762  { "Grey6", RGB_(0xf, 0xf, 0xf) },
   2763  { "Grey60", RGB_(0x99, 0x99, 0x99) },
   2764  { "Grey61", RGB_(0x9c, 0x9c, 0x9c) },
   2765  { "Grey62", RGB_(0x9e, 0x9e, 0x9e) },
   2766  { "Grey63", RGB_(0xa1, 0xa1, 0xa1) },
   2767  { "Grey64", RGB_(0xa3, 0xa3, 0xa3) },
   2768  { "Grey65", RGB_(0xa6, 0xa6, 0xa6) },
   2769  { "Grey66", RGB_(0xa8, 0xa8, 0xa8) },
   2770  { "Grey67", RGB_(0xab, 0xab, 0xab) },
   2771  { "Grey68", RGB_(0xad, 0xad, 0xad) },
   2772  { "Grey69", RGB_(0xb0, 0xb0, 0xb0) },
   2773  { "Grey7", RGB_(0x12, 0x12, 0x12) },
   2774  { "Grey70", RGB_(0xb3, 0xb3, 0xb3) },
   2775  { "Grey71", RGB_(0xb5, 0xb5, 0xb5) },
   2776  { "Grey72", RGB_(0xb8, 0xb8, 0xb8) },
   2777  { "Grey73", RGB_(0xba, 0xba, 0xba) },
   2778  { "Grey74", RGB_(0xbd, 0xbd, 0xbd) },
   2779  { "Grey75", RGB_(0xbf, 0xbf, 0xbf) },
   2780  { "Grey76", RGB_(0xc2, 0xc2, 0xc2) },
   2781  { "Grey77", RGB_(0xc4, 0xc4, 0xc4) },
   2782  { "Grey78", RGB_(0xc7, 0xc7, 0xc7) },
   2783  { "Grey79", RGB_(0xc9, 0xc9, 0xc9) },
   2784  { "Grey8", RGB_(0x14, 0x14, 0x14) },
   2785  { "Grey80", RGB_(0xcc, 0xcc, 0xcc) },
   2786  { "Grey81", RGB_(0xcf, 0xcf, 0xcf) },
   2787  { "Grey82", RGB_(0xd1, 0xd1, 0xd1) },
   2788  { "Grey83", RGB_(0xd4, 0xd4, 0xd4) },
   2789  { "Grey84", RGB_(0xd6, 0xd6, 0xd6) },
   2790  { "Grey85", RGB_(0xd9, 0xd9, 0xd9) },
   2791  { "Grey86", RGB_(0xdb, 0xdb, 0xdb) },
   2792  { "Grey87", RGB_(0xde, 0xde, 0xde) },
   2793  { "Grey88", RGB_(0xe0, 0xe0, 0xe0) },
   2794  { "Grey89", RGB_(0xe3, 0xe3, 0xe3) },
   2795  { "Grey9", RGB_(0x17, 0x17, 0x17) },
   2796  { "Grey90", RGB_(0xe5, 0xe5, 0xe5) },
   2797  { "Grey91", RGB_(0xe8, 0xe8, 0xe8) },
   2798  { "Grey92", RGB_(0xeb, 0xeb, 0xeb) },
   2799  { "Grey93", RGB_(0xed, 0xed, 0xed) },
   2800  { "Grey94", RGB_(0xf0, 0xf0, 0xf0) },
   2801  { "Grey95", RGB_(0xf2, 0xf2, 0xf2) },
   2802  { "Grey96", RGB_(0xf5, 0xf5, 0xf5) },
   2803  { "Grey97", RGB_(0xf7, 0xf7, 0xf7) },
   2804  { "Grey98", RGB_(0xfa, 0xfa, 0xfa) },
   2805  { "Grey99", RGB_(0xfc, 0xfc, 0xfc) },
   2806  { "Honeydew", RGB_(0xf0, 0xff, 0xf0) },
   2807  { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) },
   2808  { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) },
   2809  { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) },
   2810  { "Honeydew4", RGB_(0x83, 0x8b, 0x83) },
   2811  { "HotPink", RGB_(0xff, 0x69, 0xb4) },
   2812  { "HotPink1", RGB_(0xff, 0x6e, 0xb4) },
   2813  { "HotPink2", RGB_(0xee, 0x6a, 0xa7) },
   2814  { "HotPink3", RGB_(0xcd, 0x60, 0x90) },
   2815  { "HotPink4", RGB_(0x8b, 0x3a, 0x62) },
   2816  { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) },
   2817  { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) },
   2818  { "IndianRed2", RGB_(0xee, 0x63, 0x63) },
   2819  { "IndianRed3", RGB_(0xcd, 0x55, 0x55) },
   2820  { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) },
   2821  { "Indigo", RGB_(0x4b, 0x00, 0x82) },
   2822  { "Ivory", RGB_(0xff, 0xff, 0xf0) },
   2823  { "Ivory1", RGB_(0xff, 0xff, 0xf0) },
   2824  { "Ivory2", RGB_(0xee, 0xee, 0xe0) },
   2825  { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) },
   2826  { "Ivory4", RGB_(0x8b, 0x8b, 0x83) },
   2827  { "Khaki", RGB_(0xf0, 0xe6, 0x8c) },
   2828  { "Khaki1", RGB_(0xff, 0xf6, 0x8f) },
   2829  { "Khaki2", RGB_(0xee, 0xe6, 0x85) },
   2830  { "Khaki3", RGB_(0xcd, 0xc6, 0x73) },
   2831  { "Khaki4", RGB_(0x8b, 0x86, 0x4e) },
   2832  { "Lavender", RGB_(0xe6, 0xe6, 0xfa) },
   2833  { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) },
   2834  { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) },
   2835  { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) },
   2836  { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) },
   2837  { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) },
   2838  { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) },
   2839  { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) },
   2840  { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) },
   2841  { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) },
   2842  { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) },
   2843  { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) },
   2844  { "LightBlue", RGB_(0xad, 0xd8, 0xe6) },
   2845  { "LightBlue1", RGB_(0xbf, 0xef, 0xff) },
   2846  { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) },
   2847  { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) },
   2848  { "LightBlue4", RGB_(0x68, 0x83, 0x8b) },
   2849  { "LightCoral", RGB_(0xf0, 0x80, 0x80) },
   2850  { "LightCyan", RGB_(0xe0, 0xff, 0xff) },
   2851  { "LightCyan1", RGB_(0xe0, 0xff, 0xff) },
   2852  { "LightCyan2", RGB_(0xd1, 0xee, 0xee) },
   2853  { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) },
   2854  { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) },
   2855  { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) },
   2856  { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) },
   2857  { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) },
   2858  { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) },
   2859  { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) },
   2860  { "LightGoldenrodYellow", RGB_(0xfa, 0xfa, 0xd2) },
   2861  { "LightGray", RGB_(0xd3, 0xd3, 0xd3) },
   2862  { "LightGreen", RGB_(0x90, 0xee, 0x90) },
   2863  { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) },
   2864  { "LightMagenta", RGB_(0xff, 0xbb, 0xff) },
   2865  { "LightPink", RGB_(0xff, 0xb6, 0xc1) },
   2866  { "LightPink1", RGB_(0xff, 0xae, 0xb9) },
   2867  { "LightPink2", RGB_(0xee, 0xa2, 0xad) },
   2868  { "LightPink3", RGB_(0xcd, 0x8c, 0x95) },
   2869  { "LightPink4", RGB_(0x8b, 0x5f, 0x65) },
   2870  { "LightRed", RGB_(0xff, 0xbb, 0xbb) },
   2871  { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) },
   2872  { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) },
   2873  { "LightSalmon2", RGB_(0xee, 0x95, 0x72) },
   2874  { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) },
   2875  { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) },
   2876  { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) },
   2877  { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) },
   2878  { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) },
   2879  { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) },
   2880  { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) },
   2881  { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) },
   2882  { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) },
   2883  { "LightSlateGray", RGB_(0x77, 0x88, 0x99) },
   2884  { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) },
   2885  { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) },
   2886  { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) },
   2887  { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) },
   2888  { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) },
   2889  { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) },
   2890  { "LightYellow", RGB_(0xff, 0xff, 0xe0) },
   2891  { "LightYellow1", RGB_(0xff, 0xff, 0xe0) },
   2892  { "LightYellow2", RGB_(0xee, 0xee, 0xd1) },
   2893  { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) },
   2894  { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) },
   2895  { "Lime", RGB_(0x00, 0xff, 0x00) },
   2896  { "LimeGreen", RGB_(0x32, 0xcd, 0x32) },
   2897  { "Linen", RGB_(0xfa, 0xf0, 0xe6) },
   2898  { "Magenta", RGB_(0xff, 0x00, 0xff) },
   2899  { "Magenta1", RGB_(0xff, 0x0, 0xff) },
   2900  { "Magenta2", RGB_(0xee, 0x0, 0xee) },
   2901  { "Magenta3", RGB_(0xcd, 0x0, 0xcd) },
   2902  { "Magenta4", RGB_(0x8b, 0x0, 0x8b) },
   2903  { "Maroon", RGB_(0x80, 0x00, 0x00) },
   2904  { "Maroon1", RGB_(0xff, 0x34, 0xb3) },
   2905  { "Maroon2", RGB_(0xee, 0x30, 0xa7) },
   2906  { "Maroon3", RGB_(0xcd, 0x29, 0x90) },
   2907  { "Maroon4", RGB_(0x8b, 0x1c, 0x62) },
   2908  { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) },
   2909  { "MediumBlue", RGB_(0x00, 0x00, 0xcd) },
   2910  { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) },
   2911  { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) },
   2912  { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) },
   2913  { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) },
   2914  { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) },
   2915  { "MediumPurple", RGB_(0x93, 0x70, 0xdb) },
   2916  { "MediumPurple1", RGB_(0xab, 0x82, 0xff) },
   2917  { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) },
   2918  { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) },
   2919  { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) },
   2920  { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) },
   2921  { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) },
   2922  { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) },
   2923  { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) },
   2924  { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) },
   2925  { "MidnightBlue", RGB_(0x19, 0x19, 0x70) },
   2926  { "MintCream", RGB_(0xf5, 0xff, 0xfa) },
   2927  { "MistyRose", RGB_(0xff, 0xe4, 0xe1) },
   2928  { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) },
   2929  { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) },
   2930  { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) },
   2931  { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) },
   2932  { "Moccasin", RGB_(0xff, 0xe4, 0xb5) },
   2933  { "NavajoWhite", RGB_(0xff, 0xde, 0xad) },
   2934  { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) },
   2935  { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) },
   2936  { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) },
   2937  { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) },
   2938  { "Navy", RGB_(0x00, 0x00, 0x80) },
   2939  { "NavyBlue", RGB_(0x0, 0x0, 0x80) },
   2940  // Default Neovim palettes.
   2941  // Dark/light palette is used for background in dark/light color scheme and
   2942  // for foreground in light/dark color scheme.
   2943  { "NvimDarkBlue", RGB_(0x00, 0x4c, 0x73) },
   2944  { "NvimDarkCyan", RGB_(0x00, 0x73, 0x73) },
   2945  { "NvimDarkGray1", RGB_(0x07, 0x08, 0x0d) },
   2946  { "NvimDarkGray2", RGB_(0x14, 0x16, 0x1b) },
   2947  { "NvimDarkGray3", RGB_(0x2c, 0x2e, 0x33) },
   2948  { "NvimDarkGray4", RGB_(0x4f, 0x52, 0x58) },
   2949  { "NvimDarkGreen", RGB_(0x00, 0x55, 0x23) },
   2950  { "NvimDarkGrey1", RGB_(0x07, 0x08, 0x0d) },
   2951  { "NvimDarkGrey2", RGB_(0x14, 0x16, 0x1b) },
   2952  { "NvimDarkGrey3", RGB_(0x2c, 0x2e, 0x33) },
   2953  { "NvimDarkGrey4", RGB_(0x4f, 0x52, 0x58) },
   2954  { "NvimDarkMagenta", RGB_(0x47, 0x00, 0x45) },
   2955  { "NvimDarkRed", RGB_(0x59, 0x00, 0x08) },
   2956  { "NvimDarkYellow", RGB_(0x6b, 0x53, 0x00) },
   2957  { "NvimLightBlue", RGB_(0xa6, 0xdb, 0xff) },
   2958  { "NvimLightCyan", RGB_(0x8c, 0xf8, 0xf7) },
   2959  { "NvimLightGray1", RGB_(0xee, 0xf1, 0xf8) },
   2960  { "NvimLightGray2", RGB_(0xe0, 0xe2, 0xea) },
   2961  { "NvimLightGray3", RGB_(0xc4, 0xc6, 0xcd) },
   2962  { "NvimLightGray4", RGB_(0x9b, 0x9e, 0xa4) },
   2963  { "NvimLightGreen", RGB_(0xb3, 0xf6, 0xc0) },
   2964  { "NvimLightGrey1", RGB_(0xee, 0xf1, 0xf8) },
   2965  { "NvimLightGrey2", RGB_(0xe0, 0xe2, 0xea) },
   2966  { "NvimLightGrey3", RGB_(0xc4, 0xc6, 0xcd) },
   2967  { "NvimLightGrey4", RGB_(0x9b, 0x9e, 0xa4) },
   2968  { "NvimLightMagenta", RGB_(0xff, 0xca, 0xff) },
   2969  { "NvimLightRed", RGB_(0xff, 0xc0, 0xb9) },
   2970  { "NvimLightYellow", RGB_(0xfc, 0xe0, 0x94) },
   2971  { "OldLace", RGB_(0xfd, 0xf5, 0xe6) },
   2972  { "Olive", RGB_(0x80, 0x80, 0x00) },
   2973  { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) },
   2974  { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) },
   2975  { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) },
   2976  { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) },
   2977  { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) },
   2978  { "Orange", RGB_(0xff, 0xa5, 0x00) },
   2979  { "Orange1", RGB_(0xff, 0xa5, 0x0) },
   2980  { "Orange2", RGB_(0xee, 0x9a, 0x0) },
   2981  { "Orange3", RGB_(0xcd, 0x85, 0x0) },
   2982  { "Orange4", RGB_(0x8b, 0x5a, 0x0) },
   2983  { "OrangeRed", RGB_(0xff, 0x45, 0x00) },
   2984  { "OrangeRed1", RGB_(0xff, 0x45, 0x0) },
   2985  { "OrangeRed2", RGB_(0xee, 0x40, 0x0) },
   2986  { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) },
   2987  { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) },
   2988  { "Orchid", RGB_(0xda, 0x70, 0xd6) },
   2989  { "Orchid1", RGB_(0xff, 0x83, 0xfa) },
   2990  { "Orchid2", RGB_(0xee, 0x7a, 0xe9) },
   2991  { "Orchid3", RGB_(0xcd, 0x69, 0xc9) },
   2992  { "Orchid4", RGB_(0x8b, 0x47, 0x89) },
   2993  { "PaleGoldenrod", RGB_(0xee, 0xe8, 0xaa) },
   2994  { "PaleGreen", RGB_(0x98, 0xfb, 0x98) },
   2995  { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) },
   2996  { "PaleGreen2", RGB_(0x90, 0xee, 0x90) },
   2997  { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) },
   2998  { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) },
   2999  { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) },
   3000  { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) },
   3001  { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) },
   3002  { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) },
   3003  { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) },
   3004  { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) },
   3005  { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) },
   3006  { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) },
   3007  { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) },
   3008  { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) },
   3009  { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) },
   3010  { "PeachPuff", RGB_(0xff, 0xda, 0xb9) },
   3011  { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) },
   3012  { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) },
   3013  { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) },
   3014  { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) },
   3015  { "Peru", RGB_(0xcd, 0x85, 0x3f) },
   3016  { "Pink", RGB_(0xff, 0xc0, 0xcb) },
   3017  { "Pink1", RGB_(0xff, 0xb5, 0xc5) },
   3018  { "Pink2", RGB_(0xee, 0xa9, 0xb8) },
   3019  { "Pink3", RGB_(0xcd, 0x91, 0x9e) },
   3020  { "Pink4", RGB_(0x8b, 0x63, 0x6c) },
   3021  { "Plum", RGB_(0xdd, 0xa0, 0xdd) },
   3022  { "Plum1", RGB_(0xff, 0xbb, 0xff) },
   3023  { "Plum2", RGB_(0xee, 0xae, 0xee) },
   3024  { "Plum3", RGB_(0xcd, 0x96, 0xcd) },
   3025  { "Plum4", RGB_(0x8b, 0x66, 0x8b) },
   3026  { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) },
   3027  { "Purple", RGB_(0x80, 0x00, 0x80) },
   3028  { "Purple1", RGB_(0x9b, 0x30, 0xff) },
   3029  { "Purple2", RGB_(0x91, 0x2c, 0xee) },
   3030  { "Purple3", RGB_(0x7d, 0x26, 0xcd) },
   3031  { "Purple4", RGB_(0x55, 0x1a, 0x8b) },
   3032  { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) },
   3033  { "Red", RGB_(0xff, 0x00, 0x00) },
   3034  { "Red1", RGB_(0xff, 0x0, 0x0) },
   3035  { "Red2", RGB_(0xee, 0x0, 0x0) },
   3036  { "Red3", RGB_(0xcd, 0x0, 0x0) },
   3037  { "Red4", RGB_(0x8b, 0x0, 0x0) },
   3038  { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) },
   3039  { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) },
   3040  { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) },
   3041  { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) },
   3042  { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) },
   3043  { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) },
   3044  { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) },
   3045  { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) },
   3046  { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) },
   3047  { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) },
   3048  { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) },
   3049  { "Salmon", RGB_(0xfa, 0x80, 0x72) },
   3050  { "Salmon1", RGB_(0xff, 0x8c, 0x69) },
   3051  { "Salmon2", RGB_(0xee, 0x82, 0x62) },
   3052  { "Salmon3", RGB_(0xcd, 0x70, 0x54) },
   3053  { "Salmon4", RGB_(0x8b, 0x4c, 0x39) },
   3054  { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) },
   3055  { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) },
   3056  { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) },
   3057  { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) },
   3058  { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) },
   3059  { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) },
   3060  { "SeaShell", RGB_(0xff, 0xf5, 0xee) },
   3061  { "Seashell1", RGB_(0xff, 0xf5, 0xee) },
   3062  { "Seashell2", RGB_(0xee, 0xe5, 0xde) },
   3063  { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) },
   3064  { "Seashell4", RGB_(0x8b, 0x86, 0x82) },
   3065  { "Sienna", RGB_(0xa0, 0x52, 0x2d) },
   3066  { "Sienna1", RGB_(0xff, 0x82, 0x47) },
   3067  { "Sienna2", RGB_(0xee, 0x79, 0x42) },
   3068  { "Sienna3", RGB_(0xcd, 0x68, 0x39) },
   3069  { "Sienna4", RGB_(0x8b, 0x47, 0x26) },
   3070  { "Silver", RGB_(0xc0, 0xc0, 0xc0) },
   3071  { "SkyBlue", RGB_(0x87, 0xce, 0xeb) },
   3072  { "SkyBlue1", RGB_(0x87, 0xce, 0xff) },
   3073  { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) },
   3074  { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) },
   3075  { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) },
   3076  { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) },
   3077  { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) },
   3078  { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) },
   3079  { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) },
   3080  { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) },
   3081  { "SlateGray", RGB_(0x70, 0x80, 0x90) },
   3082  { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) },
   3083  { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) },
   3084  { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) },
   3085  { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) },
   3086  { "SlateGrey", RGB_(0x70, 0x80, 0x90) },
   3087  { "Snow", RGB_(0xff, 0xfa, 0xfa) },
   3088  { "Snow1", RGB_(0xff, 0xfa, 0xfa) },
   3089  { "Snow2", RGB_(0xee, 0xe9, 0xe9) },
   3090  { "Snow3", RGB_(0xcd, 0xc9, 0xc9) },
   3091  { "Snow4", RGB_(0x8b, 0x89, 0x89) },
   3092  { "SpringGreen", RGB_(0x00, 0xff, 0x7f) },
   3093  { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) },
   3094  { "SpringGreen2", RGB_(0x0, 0xee, 0x76) },
   3095  { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) },
   3096  { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) },
   3097  { "SteelBlue", RGB_(0x46, 0x82, 0xb4) },
   3098  { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) },
   3099  { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) },
   3100  { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) },
   3101  { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) },
   3102  { "Tan", RGB_(0xd2, 0xb4, 0x8c) },
   3103  { "Tan1", RGB_(0xff, 0xa5, 0x4f) },
   3104  { "Tan2", RGB_(0xee, 0x9a, 0x49) },
   3105  { "Tan3", RGB_(0xcd, 0x85, 0x3f) },
   3106  { "Tan4", RGB_(0x8b, 0x5a, 0x2b) },
   3107  { "Teal", RGB_(0x00, 0x80, 0x80) },
   3108  { "Thistle", RGB_(0xd8, 0xbf, 0xd8) },
   3109  { "Thistle1", RGB_(0xff, 0xe1, 0xff) },
   3110  { "Thistle2", RGB_(0xee, 0xd2, 0xee) },
   3111  { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) },
   3112  { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) },
   3113  { "Tomato", RGB_(0xff, 0x63, 0x47) },
   3114  { "Tomato1", RGB_(0xff, 0x63, 0x47) },
   3115  { "Tomato2", RGB_(0xee, 0x5c, 0x42) },
   3116  { "Tomato3", RGB_(0xcd, 0x4f, 0x39) },
   3117  { "Tomato4", RGB_(0x8b, 0x36, 0x26) },
   3118  { "Turquoise", RGB_(0x40, 0xe0, 0xd0) },
   3119  { "Turquoise1", RGB_(0x0, 0xf5, 0xff) },
   3120  { "Turquoise2", RGB_(0x0, 0xe5, 0xee) },
   3121  { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) },
   3122  { "Turquoise4", RGB_(0x0, 0x86, 0x8b) },
   3123  { "Violet", RGB_(0xee, 0x82, 0xee) },
   3124  { "VioletRed", RGB_(0xd0, 0x20, 0x90) },
   3125  { "VioletRed1", RGB_(0xff, 0x3e, 0x96) },
   3126  { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) },
   3127  { "VioletRed3", RGB_(0xcd, 0x32, 0x78) },
   3128  { "VioletRed4", RGB_(0x8b, 0x22, 0x52) },
   3129  { "WebGray", RGB_(0x80, 0x80, 0x80) },
   3130  { "WebGreen", RGB_(0x0, 0x80, 0x0) },
   3131  { "WebGrey", RGB_(0x80, 0x80, 0x80) },
   3132  { "WebMaroon", RGB_(0x80, 0x0, 0x0) },
   3133  { "WebPurple", RGB_(0x80, 0x0, 0x80) },
   3134  { "Wheat", RGB_(0xf5, 0xde, 0xb3) },
   3135  { "Wheat1", RGB_(0xff, 0xe7, 0xba) },
   3136  { "Wheat2", RGB_(0xee, 0xd8, 0xae) },
   3137  { "Wheat3", RGB_(0xcd, 0xba, 0x96) },
   3138  { "Wheat4", RGB_(0x8b, 0x7e, 0x66) },
   3139  { "White", RGB_(0xff, 0xff, 0xff) },
   3140  { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) },
   3141  { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) },
   3142  { "X11Green", RGB_(0x0, 0xff, 0x0) },
   3143  { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) },
   3144  { "X11Maroon", RGB_(0xb0, 0x30, 0x60) },
   3145  { "X11Purple", RGB_(0xa0, 0x20, 0xf0) },
   3146  { "Yellow", RGB_(0xff, 0xff, 0x00) },
   3147  { "Yellow1", RGB_(0xff, 0xff, 0x0) },
   3148  { "Yellow2", RGB_(0xee, 0xee, 0x0) },
   3149  { "Yellow3", RGB_(0xcd, 0xcd, 0x0) },
   3150  { "Yellow4", RGB_(0x8b, 0x8b, 0x0) },
   3151  { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) },
   3152  { NULL, 0 },
   3153 };
   3154 
   3155 /// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX),
   3156 /// else look into color_name_table to translate a color name to  its
   3157 /// hex value
   3158 ///
   3159 /// @param[in] name string value to convert to RGB
   3160 /// @param[out] idx index in color table or special value
   3161 /// return the hex value or -1 if could not find a correct value
   3162 RgbValue name_to_color(const char *name, int *idx)
   3163 {
   3164  if (name[0] == '#' && isxdigit((uint8_t)name[1]) && isxdigit((uint8_t)name[2])
   3165      && isxdigit((uint8_t)name[3]) && isxdigit((uint8_t)name[4]) && isxdigit((uint8_t)name[5])
   3166      && isxdigit((uint8_t)name[6]) && name[7] == NUL) {
   3167    // rgb hex string
   3168    *idx = kColorIdxHex;
   3169    return (RgbValue)strtol(name + 1, NULL, 16);
   3170  } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) {
   3171    *idx = kColorIdxBg;
   3172    return normal_bg;
   3173  } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) {
   3174    *idx = kColorIdxFg;
   3175    return normal_fg;
   3176  }
   3177 
   3178  int lo = 0;
   3179  int hi = ARRAY_SIZE(color_name_table) - 1;  // don't count NULL element
   3180  while (lo < hi) {
   3181    int m = (lo + hi) / 2;
   3182    int cmp = STRICMP(name, color_name_table[m].name);
   3183    if (cmp < 0) {
   3184      hi = m;
   3185    } else if (cmp > 0) {
   3186      lo = m + 1;
   3187    } else {  // found match
   3188      *idx = m;
   3189      return color_name_table[m].color;
   3190    }
   3191  }
   3192 
   3193  *idx = kColorIdxNone;
   3194  return -1;
   3195 }
   3196 
   3197 const char *coloridx_to_name(int idx, int val, char hexbuf[8])
   3198 {
   3199  if (idx >= 0) {
   3200    return color_name_table[idx].name;
   3201  }
   3202  switch (idx) {
   3203  case kColorIdxNone:
   3204    return NULL;
   3205  case kColorIdxFg:
   3206    return "fg";
   3207  case kColorIdxBg:
   3208    return "bg";
   3209  case kColorIdxHex:
   3210    snprintf(hexbuf, 7 + 1, "#%06x", val);
   3211    return hexbuf;
   3212  default:
   3213    abort();
   3214  }
   3215 }
   3216 
   3217 int name_to_ctermcolor(const char *name)
   3218 {
   3219  int i;
   3220  int off = TOUPPER_ASC(*name);
   3221  for (i = ARRAY_SIZE(color_names); --i >= 0;) {
   3222    if (off == color_names[i][0]
   3223        && STRICMP(name + 1, color_names[i] + 1) == 0) {
   3224      break;
   3225    }
   3226  }
   3227  if (i < 0) {
   3228    return -1;
   3229  }
   3230  TriState bold = kNone;
   3231  return lookup_color(i, false, &bold);
   3232 }