Repository: nvim-lualine/lualine.nvim (lua)

Issue:
Bug: Colour for diagnostics mismatch
### Self Checks
<!-- Check these boxes by placeing an X in the middle -->
- [x] I'm using the latest lualine.
- [x] I didn't find the issue in exsisting issues or prs.

### How to reproduce the problem
Install lualine with packer and call lualine.setup. 
The colors for diagnostics seem to be incorrect
![image](https://user-images.githubusercontent.com/15955811/168302053-806e084d-4e80-4642-abfc-7f621966e907.png)
![image](https://user-images.githubusercontent.com/15955811/168302123-f53a26b1-7949-4dde-bdf6-52da2be861be.png)
![image](https://user-images.githubusercontent.com/15955811/168302783-33f0415e-a121-4c99-a328-21d1c0659e56.png)

Taken from lualine source, it seems that it should respect those hi groups.
```Lua
function M.apply_default_colors(opts)
  local default_diagnostics_color = {
    error = {
      fg = utils.extract_color_from_hllist(
        'fg',
        { 'DiagnosticError', 'LspDiagnosticsDefaultError', 'DiffDelete' },
        '#e32636'
      ),
    },
    warn = { 
      fg = utils.extract_color_from_hllist(
        'fg',
        { 'DiagnosticWarn', 'LspDiagnosticsDefaultWarning', 'DiffText' },
        '#ffa500' 
      ),
    },
    info = {
      fg = utils.extract_color_from_hllist(
        'fg',
        { 'DiagnosticInfo', 'LspDiagnosticsDefaultInformation', 'Normal' },
        '#ffffff'
      ),
    },
    hint = {
      fg = utils.extract_color_from_hllist(
        'fg',
        { 'DiagnosticHint', 'LspDiagnosticsDefaultHint', 'DiffChange' },
        '#273faf'
      ),
    },
  }
  opts.diagnostics_color = vim.tbl_deep_extend('keep', opts.diagnostics_color or {}, default_diagnostics_color)
end
```

### Expected behaviour
Color should be respected

### Actual behaviour
Instead lualine renders weird dark grey.

### Minimal config to reproduce the issue
No configuration has been applied just calling lualine.setup()

### Aditional information
using everforest theme.

Pull request:
> **Note**
> Marking as draft because requires #834 to properly handle the `sp` highlight scope.
> Right now this PR contains both commits to prevent tests from failing.

---

### What

Allows passing a list of scopes to `extract_color_from_hllist()`, where each scope is tried sequentially until a matching color is found in the highlight group (return on first match).

The change is fully backwards compatible; if the `scope` parameter is a string, the function can handle that and only tries that one scope.

### Why

My main motivation is to allow a sensible fallback to the <ins>underline</ins> color in `config.(default_)diagnostics_color` in case any of the `DiagnosticError`, `DiagnosticWarn`, `DiagnosticHint` or `DiagnosticInfo` highlight groups do not have a foreground color set.

In many IDEs, it is a [common practice](https://user-images.githubusercontent.com/7727148/38758132-7284e58a-3f24-11e8-8e39-627e6916e9ec.PNG) to highlight diagnostics by underlining the context, instead of changing its foreground color. Due to Vim's legacy of supporting terminals without capabilities such as _italics_, **bold** or <ins>underline</ins>, this has never become a widespread pattern in our favourite text editor. Nevertheless, some (fairly popular) color schemes — such as [everforest](https://github.com/sainnhe/everforest), [gruvbox-material](https://github.com/sainnhe/gruvbox-material) or [sonokai](https://github.com/sainnhe/sonokai) — do apply underline styles to these highlight groups, and right now Lualine can't handle this practice very well:

<img width="624" alt="image" src="https://user-images.githubusercontent.com/3299086/189524284-fd6fb4d5-dc59-43e6-ae78-c5952eb49c47.png">
<img width="332" alt="image" src="https://user-images.githubusercontent.com/3299086/189480440-7c45435c-939b-4017-805b-02bfc8e2e371.png">

With this change, Lualine is now able to pick a sensible color for diagnostic signs, when diagnostic highlight groups use the underline highlight attribute without a foreground color:

<img width="624" alt="image" src="https://user-images.githubusercontent.com/3299086/189524798-b9c05860-e566-4bad-b676-7bc6059c96e8.png">

Also, fixes #681

Patch:
diff --git a/lua/lualine/components/diagnostics/config.lua b/lua/lualine/components/diagnostics/config.lua
index 6b80fffe9..bfe3544de 100644
--- a/lua/lualine/components/diagnostics/config.lua
+++ b/lua/lualine/components/diagnostics/config.lua
@@ -26,28 +26,28 @@ function M.apply_default_colors(opts)
   local default_diagnostics_color = {
     error = {
       fg = utils.extract_color_from_hllist(
-        'fg',
+        { 'fg', 'sp' },
         { 'DiagnosticError', 'LspDiagnosticsDefaultError', 'DiffDelete' },
         '#e32636'
       ),
     },
     warn = {
       fg = utils.extract_color_from_hllist(
-        'fg',
+        { 'fg', 'sp' },
         { 'DiagnosticWarn', 'LspDiagnosticsDefaultWarning', 'DiffText' },
         '#ffa500'
       ),
     },
     info = {
       fg = utils.extract_color_from_hllist(
-        'fg',
+        { 'fg', 'sp' },
         { 'DiagnosticInfo', 'LspDiagnosticsDefaultInformation', 'Normal' },
         '#ffffff'
       ),
     },
     hint = {
       fg = utils.extract_color_from_hllist(
-        'fg',
+        { 'fg', 'sp' },
         { 'DiagnosticHint', 'LspDiagnosticsDefaultHint', 'DiffChange' },
         '#273faf'
       ),
diff --git a/lua/lualine/highlight.lua b/lua/lualine/highlight.lua
index 20764b5e4..f297d8be4 100644
--- a/lua/lualine/highlight.lua
+++ b/lua/lualine/highlight.lua
@@ -78,6 +78,7 @@ function M.get_lualine_hl(name)
     local hl_def = {
       fg = hl.fg ~= 'None' and vim.deepcopy(hl.fg) or nil,
       bg = hl.bg ~= 'None' and vim.deepcopy(hl.bg) or nil,
+      sp = hl.sp ~= 'None' and vim.deepcopy(hl.sp) or nil,
     }
     if hl.gui then
       for _, flag in ipairs(vim.split(hl.gui, ',')) do
diff --git a/lua/lualine/utils/utils.lua b/lua/lualine/utils/utils.lua
index bc28b431c..2f74c3e24 100644
--- a/lua/lualine/utils/utils.lua
+++ b/lua/lualine/utils/utils.lua
@@ -5,7 +5,7 @@ local M = {}
 -- Note for now only works for termguicolors scope can be bg or fg or any other
 -- attr parameter like bold/italic/reverse
 ---@param color_group string hl_group name
----@param scope       string bg | fg
+---@param scope       string bg | fg | sp
 ---@return table|string returns #rrggbb formatted color when scope is specified
 ----                       or complete color table when scope isn't specified
 function M.extract_highlight_colors(color_group, scope)
@@ -23,6 +23,10 @@ function M.extract_highlight_colors(color_group, scope)
       color.fg = string.format('#%06x', color.foreground)
       color.foreground = nil
     end
+    if color.special ~= nil then
+      color.sp = string.format('#%06x', color.special)
+      color.special = nil
+    end
   end
   if scope then
     return color[scope]
@@ -32,23 +36,26 @@ end
 
 --- retrieves color value from highlight group name in syntax_list
 --- first present highlight is returned
----@param scope string
+---@param scope string|table
 ---@param syntaxlist table
 ---@param default string
 ---@return string|nil
 function M.extract_color_from_hllist(scope, syntaxlist, default)
+  scope = type(scope) == 'string' and { scope } or scope
   for _, highlight_name in ipairs(syntaxlist) do
     if vim.fn.hlexists(highlight_name) ~= 0 then
       local color = M.extract_highlight_colors(highlight_name)
-      if color.reverse then
-        if scope == 'bg' then
-          scope = 'fg'
-        else
-          scope = 'bg'
+      for _, sc in ipairs(scope) do
+        if color.reverse then
+          if sc == 'bg' then
+            sc = 'fg'
+          else
+            sc = 'bg'
+          end
+        end
+        if color[sc] then
+          return color[sc]
         end
-      end
-      if color[scope] then
-        return color[scope]
       end
     end
   end


Test patch:
diff --git a/tests/spec/utils_spec.lua b/tests/spec/utils_spec.lua
index 59af60cdb..cc44d07b6 100644
--- a/tests/spec/utils_spec.lua
+++ b/tests/spec/utils_spec.lua
@@ -11,11 +11,11 @@ describe('Utils', function()
   local utils = require('lualine.utils.utils')
 
   it('can retrive highlight groups', function()
-    local hl2 = { fg = '#aabbcc', bg = '#889977', reverse = true }
+    local hl2 = { fg = '#aabbcc', bg = '#889977', sp = '#997788', reverse = true, undercurl = true }
     -- handles non existing hl groups
     eq(utils.extract_highlight_colors('hl2'), nil)
     -- create highlight
-    vim.cmd(string.format('hi hl2 guifg=%s guibg=%s gui=reverse', hl2.fg, hl2.bg))
+    vim.cmd(string.format('hi hl2 guifg=%s guibg=%s guisp=%s gui=reverse,undercurl', hl2.fg, hl2.bg, hl2.sp))
     -- Can retrieve entire highlight table
     eq(utils.extract_highlight_colors('hl2'), hl2)
     -- Can retrieve specific parts of highlight
@@ -24,6 +24,41 @@ describe('Utils', function()
     vim.cmd('hi clear hl2')
   end)
 
+  it('can extract individual highlight color', function()
+    local fg_clr = '#aabbcc'
+    local bg_clr = '#889977'
+    local sp_clr = '#997788'
+    local def_clr = '#ff0000'
+    local hl_std = { fg = fg_clr, bg = bg_clr }
+    local hl_rvs = { fg = fg_clr, bg = bg_clr, reverse = true }
+    local hl_ul = { sp = sp_clr, undercurl = true }
+    local hl_ul_rvs = { fg = fg_clr, bg = bg_clr, sp = sp_clr, reverse = true, undercurl = true }
+    -- create highlights
+    vim.cmd(string.format('hi hl_std guifg=%s guibg=%s', hl_std.fg, hl_std.bg))
+    vim.cmd(string.format('hi hl_rvs guifg=%s guibg=%s gui=reverse', hl_rvs.fg, hl_rvs.bg))
+    vim.cmd(string.format('hi hl_ul guisp=%s gui=undercurl', hl_ul.sp))
+    vim.cmd(string.format('hi hl_ul_rvs guifg=%s guibg=%s guisp=%s gui=reverse,undercurl', hl_ul_rvs.fg, hl_ul_rvs.bg, hl_ul_rvs.sp))
+    -- Can extract color from primary highlight group
+    eq(utils.extract_color_from_hllist('fg', {'hl_std','hl_ul'}, def_clr), fg_clr)
+    -- Can extract color from fallback highlight group
+    eq(utils.extract_color_from_hllist('fg', {'hl_noexist','hl_std'}, def_clr), fg_clr)
+    -- Can fall back to default color on nonexistent color
+    eq(utils.extract_color_from_hllist('fg', {'hl_ul'}, def_clr), def_clr)
+    -- Can fall back to default color on nonexistent highlight group
+    eq(utils.extract_color_from_hllist('fg', {'hl_noexist'}, def_clr), def_clr)
+    -- Can extract fallback color
+    eq(utils.extract_color_from_hllist({'fg','sp'}, {'hl_ul'}, def_clr), sp_clr)
+    -- Can extract reverse color
+    eq(utils.extract_color_from_hllist('fg', {'hl_rvs'}, def_clr), bg_clr)
+    -- Can extract fallback reverse color
+    eq(utils.extract_color_from_hllist({'sp','fg'}, {'hl_rvs'}, def_clr), bg_clr)
+    -- clear highlights
+    vim.cmd('hi clear hl_std')
+    vim.cmd('hi clear hl_rvs')
+    vim.cmd('hi clear hl_ul')
+    vim.cmd('hi clear hl_ul_rvs')
+  end)
+
   it('can shrink list with holes', function()
     local list_with_holes = {
       '2',