[Deprection] Enhance: Provide an uniform interface for color options

- Now all color related options and themes color definition have same
  semantics. They can either be a table containing values like bg,fg,gui
  or a string. When it's a string it's treated as a highlight group name
  and newly created group is linked to that group.
- Using string values to define foreground colors in options like
  color_error/color_added in diagnostics and diff component has been
  deprecated.
- Added a decent way of notifying affected users of deprecations and
  breaking changes.
This commit is contained in:
shadmansaleh 2021-08-09 13:53:42 +06:00
parent d541574028
commit f68d81d351
9 changed files with 224 additions and 61 deletions

View File

@ -16,6 +16,9 @@ lualine theme at lua/lualine/themes/{your_colorscheme}.lua in you repo.
To create a custom theme you need to define a colorscheme for each of vim's modes. Each mode has a `fg` and `bg` field for every lualine section.
To specify colors you can use #rrggbb/color_name(like: red)/cterm_color(0-255).
Instead of defining colors as table containing values you can define it as a
string. This string is considered a highlight group name and lualines highlight
is linked to that group.
You can add special effects with `gui`.
Though the example shows a,b,c being set you can specify theme for x, y, z too.

View File

@ -39,13 +39,11 @@ local Component = {
create_option_highlights = function(self)
-- set custom highlights
if type(self.options.color) == 'table' then
if self.options.color then
self.options.color_highlight = highlight.create_component_highlight_group(
self.options.color,
self.options.component_name,
self.options)
elseif type(self.options.color) == 'string' then
self.options.color_highlight_link = self.options.color
end
end,
@ -84,9 +82,6 @@ local Component = {
if self.options.color_highlight then
self.status = highlight.component_format_highlight(
self.options.color_highlight) .. self.status
elseif self.options.color_highlight_link then
self.status = '%#' .. self.options.color_highlight_link .. '#' ..
self.status
end
self.status = self.status .. default_highlight
end,

View File

@ -2,6 +2,7 @@
-- MIT license, see LICENSE for more details.
local highlight = require('lualine.highlight')
local utils = require('lualine.utils.utils')
local utils_notices = require('lualine.utils.notices')
local Diagnostics = require('lualine.component'):new()
@ -14,6 +15,34 @@ Diagnostics.default_colors = {
}
-- LuaFormatter on
local function color_deprecation_notice(color, opt_name)
utils_notices.add_notice(string.format([[
### Diagnostics component
Using option `%s` as string to set foreground color has been deprecated
and will soon be removed. Now this option has same semantics as regular
`color` option for components. Means now you can set bg/fg or both.
String value is still valid but it's interpreted differemtly. When a
string is used for this option it's treated as a highlight group name.
In that case `%s` will be linked to that highlight group.
You have something like this in your config.
```lua
{'diagnostics',
%s = '%s',
}
```
You'll have to change it to this to retain previous behavior
```lua
{'diagnostics',
%s = { fg = '%s'},
}
```
]], opt_name, opt_name, opt_name, color, opt_name, color))
end
-- Initializer
Diagnostics.new = function(self, options, child)
local new_diagnostics = self._parent:new(options, child or Diagnostics)
@ -41,43 +70,59 @@ Diagnostics.new = function(self, options, child)
new_diagnostics.last_update = ''
-- apply colors
if not new_diagnostics.options.color_error then
new_diagnostics.options.color_error =
new_diagnostics.options.color_error = {fg =
utils.extract_highlight_colors('LspDiagnosticsDefaultError', 'fg') or
utils.extract_highlight_colors('DiffDelete', 'fg') or
Diagnostics.default_colors.error
Diagnostics.default_colors.error }
elseif type(new_diagnostics.options.color_error) == 'string'
and vim.fn.hlexists(new_diagnostics.options.color_error) == 0 then
new_diagnostics.options.color_error = {fg = new_diagnostics.options.color_error}
color_deprecation_notice(new_diagnostics.options.color_error.fg, 'color_error')
end
if not new_diagnostics.options.color_warn then
new_diagnostics.options.color_warn =
new_diagnostics.options.color_warn = {fg =
utils.extract_highlight_colors('LspDiagnosticsDefaultWarning', 'fg') or
utils.extract_highlight_colors('DiffText', 'fg') or
Diagnostics.default_colors.warn
Diagnostics.default_colors.warn }
elseif type(new_diagnostics.options.color_warn) == 'string'
and vim.fn.hlexists(new_diagnostics.options.color_warn) == 0 then
new_diagnostics.options.color_warn = {fg = new_diagnostics.options.color_warn}
color_deprecation_notice(new_diagnostics.options.color_warn.fg, 'color_warn')
end
if not new_diagnostics.options.color_info then
new_diagnostics.options.color_info =
new_diagnostics.options.color_info = {fg =
utils.extract_highlight_colors('LspDiagnosticsDefaultInformation', 'fg') or
utils.extract_highlight_colors('Normal', 'fg') or
Diagnostics.default_colors.info
Diagnostics.default_colors.info}
elseif type(new_diagnostics.options.color_info) == 'string'
and vim.fn.hlexists(new_diagnostics.options.color_info) == 0 then
new_diagnostics.options.color_info = {fg = new_diagnostics.options.color_info}
color_deprecation_notice(new_diagnostics.options.color_info.fg, 'color_info')
end
if not new_diagnostics.options.color_hint then
new_diagnostics.options.color_hint =
new_diagnostics.options.color_hint = {fg =
utils.extract_highlight_colors('LspDiagnosticsDefaultHint', 'fg') or
utils.extract_highlight_colors('DiffChange', 'fg') or
Diagnostics.default_colors.hint
Diagnostics.default_colors.hint}
elseif type(new_diagnostics.options.color_hint) == 'string'
and vim.fn.hlexists(new_diagnostics.options.color_hint) == 0 then
new_diagnostics.options.color_hint = {fg = new_diagnostics.options.color_hint}
color_deprecation_notice(new_diagnostics.options.color_hint.fg, 'color_hint')
end
if new_diagnostics.options.colored then
new_diagnostics.highlight_groups = {
error = highlight.create_component_highlight_group(
{fg = new_diagnostics.options.color_error}, 'diagnostics_error',
new_diagnostics.options.color_error, 'diagnostics_error',
new_diagnostics.options),
warn = highlight.create_component_highlight_group(
{fg = new_diagnostics.options.color_warn}, 'diagnostics_warn',
new_diagnostics.options.color_warn, 'diagnostics_warn',
new_diagnostics.options),
info = highlight.create_component_highlight_group(
{fg = new_diagnostics.options.color_info}, 'diagnostics_info',
new_diagnostics.options.color_info, 'diagnostics_info',
new_diagnostics.options),
hint = highlight.create_component_highlight_group(
{fg = new_diagnostics.options.color_hint}, 'diagnostics_hint',
new_diagnostics.options.color_hint, 'diagnostics_hint',
new_diagnostics.options)
}
end

View File

@ -1,6 +1,7 @@
-- Copyright (c) 2020-2021 shadmansaleh
-- MIT license, see LICENSE for more details.
local utils = require 'lualine.utils.utils'
local utils_notices = require('lualine.utils.notices')
local highlight = require 'lualine.highlight'
local Job = require'lualine.utils.job'
@ -22,6 +23,33 @@ Diff.default_colors = {
local diff_cache = {} -- Stores last known value of diff of a buffer
local function color_deprecation_notice(color, opt_name)
utils_notices.add_notice(string.format([[
### Diff component
Using option `%s` as string to set foreground color has been deprecated
and will soon be removed. Now this option has same semantics as regular
`color` option for components. Means now you can set bg/fg or both.
String value is still valid but it's interpreted differemtly. When a
string is used for this option it's treated as a highlight group name.
In that case `%s` will be linked to that highlight group.
You have something like this in your config.
```lua
{'diff',
%s = '%s',
}
```
You'll have to change it to this to retain previous behavior
```lua
{'diff',
%s = { fg = '%s'},
}
```
]], opt_name, opt_name, opt_name, color, opt_name, color))
end
-- Initializer
Diff.new = function(self, options, child)
local new_instance = self._parent:new(options, child or Diff)
@ -34,32 +62,44 @@ Diff.new = function(self, options, child)
end
-- apply colors
if not new_instance.options.color_added then
new_instance.options.color_added = utils.extract_highlight_colors('DiffAdd',
'fg') or
Diff.default_colors.added
new_instance.options.color_added = {fg =
utils.extract_highlight_colors('DiffAdd', 'fg') or
Diff.default_colors.added}
elseif type(new_instance.options.color_added) == 'string'
and vim.fn.hlexists(new_instance.options.color_added) == 0 then
new_instance.options.color_added = {fg = new_instance.options.color_added}
color_deprecation_notice(new_instance.options.color_added.fg, 'color_added')
end
if not new_instance.options.color_modified then
new_instance.options.color_modified =
new_instance.options.color_modified = {fg =
utils.extract_highlight_colors('DiffChange', 'fg') or
Diff.default_colors.modified
Diff.default_colors.modified}
elseif type(new_instance.options.color_modified) == 'string'
and vim.fn.hlexists(new_instance.options.color_modified) == 0 then
new_instance.options.color_modified = {fg = new_instance.options.color_modified}
color_deprecation_notice(new_instance.options.color_modified.fg, 'color_modified')
end
if not new_instance.options.color_removed then
new_instance.options.color_removed =
new_instance.options.color_removed = {fg =
utils.extract_highlight_colors('DiffDelete', 'fg') or
Diff.default_colors.removed
Diff.default_colors.removed}
elseif type(new_instance.options.color_removed) == 'string'
and vim.fn.hlexists(new_instance.options.color_removed) == 0 then
new_instance.options.color_removed = {fg = new_instance.options.color_removed}
color_deprecation_notice(new_instance.options.color_removed.fg, 'color_removed')
end
-- create highlights and save highlight_name in highlights table
if new_instance.options.colored then
new_instance.highlights = {
added = highlight.create_component_highlight_group(
{fg = new_instance.options.color_added}, 'diff_added',
new_instance.options.color_added, 'diff_added',
new_instance.options),
modified = highlight.create_component_highlight_group(
{fg = new_instance.options.color_modified}, 'diff_modified',
new_instance.options.color_modified, 'diff_modified',
new_instance.options),
removed = highlight.create_component_highlight_group(
{fg = new_instance.options.color_removed}, 'diff_removed',
new_instance.options.color_removed, 'diff_removed',
new_instance.options)
}
end

View File

@ -20,31 +20,36 @@ local function sanitize_color(color)
end
end
function M.highlight(name, foreground, background, gui, reload)
foreground = sanitize_color(foreground)
background = sanitize_color(background)
local command = {'highlight', name}
if foreground and foreground ~= 'none' then
table.insert(command, 'guifg=' .. foreground)
if cterm_colors then
table.insert(command,
'ctermfg=' .. cterm_colors.get_cterm_color(foreground))
function M.highlight(name, foreground, background, gui, link, reload)
local command = {'highlight!'}
if link and #link > 0 then
vim.list_extend(command, {'link', name, link})
else
foreground = sanitize_color(foreground)
background = sanitize_color(background)
table.insert(command, name)
if foreground and foreground ~= 'none' then
table.insert(command, 'guifg=' .. foreground)
if cterm_colors then
table.insert(command,
'ctermfg=' .. cterm_colors.get_cterm_color(foreground))
end
end
end
if background and background ~= 'none' then
table.insert(command, 'guibg=' .. background)
if cterm_colors then
table.insert(command,
'ctermbg=' .. cterm_colors.get_cterm_color(background))
if background and background ~= 'none' then
table.insert(command, 'guibg=' .. background)
if cterm_colors then
table.insert(command,
'ctermbg=' .. cterm_colors.get_cterm_color(background))
end
end
if gui then
table.insert(command, 'cterm=' .. gui)
table.insert(command, 'gui=' .. gui)
end
end
if gui then
table.insert(command, 'cterm=' .. gui)
table.insert(command, 'gui=' .. gui)
end
vim.cmd(table.concat(command, ' '))
if not reload then
utils.save_highlight(name, {name, foreground, background, gui, true})
utils.save_highlight(name, {name, foreground, background, gui, link, true})
end
end
@ -55,10 +60,15 @@ function M.create_highlight_groups(theme)
cterm_colors = require 'lualine.utils.cterm_colors'
end
for mode, sections in pairs(theme) do
for section, colorscheme in pairs(sections) do
for section, color in pairs(sections) do
local highlight_group_name = {'lualine', section, mode}
M.highlight(table.concat(highlight_group_name, '_'), colorscheme.fg,
colorscheme.bg, colorscheme.gui)
if type(color) == 'string' then -- link to a highlight group
M.highlight(table.concat(highlight_group_name, '_'), nil,
nil, nil, color)
else -- Define a new highlight
M.highlight(table.concat(highlight_group_name, '_'), color.fg,
color.bg, color.gui, nil)
end
end
end
end
@ -105,12 +115,18 @@ function M.create_component_highlight_group(color, highlight_tag, options)
highlight_tag = highlight_tag .. '_' .. tostring(tag_id)
tag_id = tag_id + 1
end
if type(color) == 'string' then
local highlight_group_name = table.concat(
{'lualine', highlight_tag, 'no_mode'}, '_')
M.highlight(highlight_group_name, nil, nil, nil, color) -- l8nk to group
return highlight_group_name
end
if color.bg and color.fg then
-- When bg and fg are both present we donn't need to set highlighs for
-- each mode as they will surely look the same. So we can work without options
local highlight_group_name = table.concat(
{'lualine', highlight_tag, 'no_mode'}, '_')
M.highlight(highlight_group_name, color.fg, color.bg, color.gui)
M.highlight(highlight_group_name, color.fg, color.bg, color.gui, nil)
return highlight_group_name
end
@ -134,11 +150,13 @@ function M.create_component_highlight_group(color, highlight_tag, options)
-- Check if it's same as normal mode if it is no need to create aditional highlight
if mode ~= 'normal' then
if bg ~= normal_hl.bg or fg ~= normal_hl.fg then
M.highlight(table.concat(highlight_group_name, '_'), fg, bg, color.gui)
M.highlight(table.concat(highlight_group_name, '_'), fg, bg, color.gui,
nil)
end
else
normal_hl = {bg = bg, fg = fg}
M.highlight(table.concat(highlight_group_name, '_'), fg, bg, color.gui)
M.highlight(table.concat(highlight_group_name, '_'), fg, bg, color.gui,
nil)
end
end
return options.self.section .. '_' .. highlight_tag
@ -238,7 +256,7 @@ function M.get_transitional_highlights(left_section_data, right_section_data,
if reverse then fg, bg = bg, fg end
if not fg or not bg then return '' end -- Color retrieval failed
if bg == fg then return '' end -- Separatoe won't be visible anyway
M.highlight(highlight_name, fg, bg)
M.highlight(highlight_name, fg, bg, nil)
end
return '%#' .. highlight_name .. '#'
end

View File

@ -4,6 +4,7 @@ local highlight = require('lualine.highlight')
local loader = require('lualine.utils.loader')
local utils_section = require('lualine.utils.section')
local utils = require('lualine.utils.utils')
local utils_notices = require('lualine.utils.notices')
local config_module = require('lualine.config')
local config = config_module.config
@ -218,12 +219,14 @@ local function setup_augroup()
end
local function setup(user_config)
utils_notices.clear_notices()
config = config_module.apply_configuration(user_config)
setup_augroup()
setup_theme()
loader.load_all(config)
set_statusline()
set_tabline()
utils_notices.notice_message_startup()
end
return {

View File

@ -0,0 +1,44 @@
-- To provide notices for user
local M = {}
local notices = {}
function M.add_notice(notice)
if type(notice) == 'string' then notice = vim.split(notice, "\n") end
table.insert(notices, notice)
end
function M.notice_message_startup()
if #notices > 0 then
vim.cmd('command! -nargs=0 LualineNotices lua require"lualine.utils.notices".show_notices()')
vim.schedule(function()
vim.notify(
"lualine: There are some issues with your config. Run :LualineNotices for details",
vim.log.levels.WARN, {})
end)
end
end
function M.show_notices()
vim.cmd [[
:silent! new
:silent! setl ft=markdown bt=nofile nobuflisted bh=wipe
:silent! nnoremap <silent><buffer> q <cmd>bd<cr>
:silent! normal ggdG
]]
local ok, _ = pcall(vim.api.nvim_buf_set_name, 0, "Lualine Notices")
if not ok then
vim.notify("Lualine Notices is already open in another window",
vim.log.levels.ERROR, {})
vim.cmd('normal q')
return
end
local notice = vim.tbl_flatten(notices)
vim.fn.append(0, notice)
vim.api.nvim_win_set_cursor(0, {1, 0})
end
function M.clear_notices()
notices = {}
end
return M

View File

@ -47,10 +47,21 @@ describe('Component:', function()
.component_name,
comp1.options)
hl.create_component_highlight_group:revert()
local opts2 = build_component_opts({color = 'MyHl'})
color = 'MyHl'
local opts2 = build_component_opts({color = color})
stub(hl, 'create_component_highlight_group')
hl.create_component_highlight_group.returns('MyCompLinkedHl')
local comp2 = require('lualine.components.special.function_component'):new(
opts2)
eq('MyHl', comp2.options.color_highlight_link)
eq('MyCompLinkedHl', comp2.options.color_highlight)
-- color highlight wan't in options when create_comp_hl was
-- called so remove it before assert
comp2.options.color_highlight = nil
assert.stub(hl.create_component_highlight_group).was_called_with(color,
comp2.options
.component_name,
comp2.options)
hl.create_component_highlight_group:revert()
end)
it('can draw', function()
@ -186,7 +197,9 @@ describe('Component:', function()
padding = 0,
color = 'MyHl'
})
assert_component(nil, opts, '%#MyHl#test')
local comp = require('lualine.components.special.function_component'):new(opts)
local custom_link_hl_name = 'lualine_'..comp.options.component_name..'_no_mode'
eq('%#'..custom_link_hl_name..'#test', comp:draw(opts.hl))
local opts2 = build_component_opts(
{
component_separators = {'', ''},

View File

@ -8,7 +8,7 @@ describe('Utils', function()
local utils = require('lualine.utils.utils')
it('can save and restore highlights', function()
local hl1 = {'hl1', '#122233', '#445566', 'italic', true}
local hl1 = {'hl1', '#122233', '#445566', 'italic', nil, true}
utils.save_highlight(hl1[1], hl1)
-- highlight loaded in loaded_highlights table
eq(utils.loaded_highlights[hl1[1]], hl1)
@ -101,9 +101,11 @@ describe('Section genarator', function()
require('lualine.components.special.function_component'):new(opts_colored),
require('lualine.components.special.function_component'):new(opts)
}
local highlight_name2 = 'lualine_'..section[2].options.component_name..'_no_mode'
-- Removes separator on string color
eq(
'%#lualine_MySection_normal# test %#lualine_MySection_normal#%#MyColor#'
'%#lualine_MySection_normal# test %#lualine_MySection_normal#%#'
..highlight_name2..'#'
.. ' test %#lualine_MySection_normal# test %#lualine_MySection_normal#',
sec.draw_section(section, 'MySection'))
section[2] =
@ -119,7 +121,7 @@ describe('Section genarator', function()
section[2] =
require('lua.lualine.components.special.function_component'):new(
opts_colored3)
local highlight_name2 =
highlight_name2 =
'%#lualine_c_' .. section[2].options.component_name .. '_normal#'
-- Doesn't remove separator on color without bg
eq('%#lualine_MySection_normal# test %#lualine_MySection_normal#' ..