diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f53a991..dde8588 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/lua/lualine/component.lua b/lua/lualine/component.lua index 88333ef..b52511b 100644 --- a/lua/lualine/component.lua +++ b/lua/lualine/component.lua @@ -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, diff --git a/lua/lualine/components/diagnostics.lua b/lua/lualine/components/diagnostics.lua index ff92aae..51b6446 100644 --- a/lua/lualine/components/diagnostics.lua +++ b/lua/lualine/components/diagnostics.lua @@ -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 diff --git a/lua/lualine/components/diff.lua b/lua/lualine/components/diff.lua index 1a18459..57b6bfb 100644 --- a/lua/lualine/components/diff.lua +++ b/lua/lualine/components/diff.lua @@ -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 diff --git a/lua/lualine/highlight.lua b/lua/lualine/highlight.lua index c39f69e..b9b3f63 100644 --- a/lua/lualine/highlight.lua +++ b/lua/lualine/highlight.lua @@ -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 diff --git a/lua/lualine/init.lua b/lua/lualine/init.lua index f56f9a6..a2574eb 100644 --- a/lua/lualine/init.lua +++ b/lua/lualine/init.lua @@ -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 { diff --git a/lua/lualine/utils/notices.lua b/lua/lualine/utils/notices.lua new file mode 100644 index 0000000..98df9af --- /dev/null +++ b/lua/lualine/utils/notices.lua @@ -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 q bd + :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 diff --git a/lua/tests/spec/component_spec.lua b/lua/tests/spec/component_spec.lua index 6c4845a..fe4b28f 100644 --- a/lua/tests/spec/component_spec.lua +++ b/lua/tests/spec/component_spec.lua @@ -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 = {'', ''}, diff --git a/lua/tests/spec/utils_spec.lua b/lua/tests/spec/utils_spec.lua index f290edb..b4f9d76 100644 --- a/lua/tests/spec/utils_spec.lua +++ b/lua/tests/spec/utils_spec.lua @@ -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#' ..