-- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local M = { } local utils_colors = require "lualine.utils.cterm_colors" local utils = require 'lualine.utils.utils' local section_highlight_map = {x = 'c', y = 'b', z = 'a'} local loaded_highlights = {} -- @description: clears Highlights in loaded_highlights function M.clear_highlights() for no, highlight_name in ipairs(loaded_highlights)do if highlight_name and #highlight_name > 0 and utils.highlight_exists(highlight_name) then vim.cmd('highlight clear ' .. highlight_name) end loaded_highlights[no] = nil end end local function highlight (name, foreground, background, gui) local command = { 'highlight', name, } if foreground then table.insert(command, 'ctermfg=' .. (foreground[2] or (foreground ~= 'none' and utils_colors.get_cterm_color(foreground)) or 'none')) table.insert(command, 'guifg=' .. (foreground[1] or foreground)) end if background then table.insert(command, 'ctermbg=' .. (background[2] or (background ~= 'none' and utils_colors.get_cterm_color(background)) or 'none')) table.insert(command, 'guibg=' .. (background[1] or background)) end if gui then table.insert(command, 'cterm=' .. (gui or 'none')) table.insert(command, 'gui=' .. (gui or 'none')) end vim.cmd(table.concat(command, ' ')) table.insert(loaded_highlights, name) end local function apply_defaults_to_theme(theme) local modes = {'insert', 'visual', 'replace', 'command', 'terminal', 'inactive'} for _, mode in ipairs(modes) do if not theme[mode] then theme[mode] = theme['normal'] else for section_name, section in pairs(theme['normal']) do theme[mode][section_name] = (theme[mode][section_name] or section) end end end return theme end function M.create_highlight_groups(theme) apply_defaults_to_theme(theme) for mode, sections in pairs(theme) do for section, colorscheme in pairs(sections) do local highlight_group_name = { 'lualine', section, mode } highlight(table.concat(highlight_group_name, '_'), colorscheme.fg, colorscheme.bg, colorscheme.gui) end end end -- @description: adds '_mode' at end of highlight_group -- @param highlight_group:(string) name of highlight group -- @return: (string) highlight group name with mode local function append_mode(highlight_group) local mode = require('lualine.components.mode')() if mode == 'VISUAL' or mode == 'V-BLOCK' or mode == 'V-LINE' or mode == 'SELECT' or mode == 'S-LINE' or mode == 'S-BLOCK'then highlight_group = highlight_group .. '_visual' elseif mode == 'REPLACE' or mode == 'V-REPLACE' then highlight_group = highlight_group .. '_replace' elseif mode == 'INSERT' then highlight_group = highlight_group .. '_insert' elseif mode == 'COMMAND' or mode == 'EX' or mode == 'MORE' or mode == 'CONFIRM'then highlight_group = highlight_group .. '_command' elseif mode == 'TERMINAL' then highlight_group = highlight_group .. '_terminal' else highlight_group = highlight_group .. '_normal' end return highlight_group end -- Create highlight group with fg bg and gui from theme -- section and theme are extracted from @options.self table -- @@color has to be { fg = "#rrggbb", bg="#rrggbb" gui = "effect" } -- all the color elements are optional if fg or bg is not given options must be provided -- So fg and bg can default the themes colors -- @@highlight_tag is unique tag for highlight group -- returns the name of highlight group -- @@options is parameter of component.init() function -- @return: (string) unique name that can be used by component_format_highlight -- to retrive highlight group function M.create_component_highlight_group(color , highlight_tag, options) 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'}, '_') highlight(highlight_group_name, color.fg, color.bg, color.gui) return highlight_group_name end local modes = {'normal', 'insert', 'visual', 'replace', 'command', 'terminal', 'inactive'} local normal_hl -- convert lualine_a -> a before setting section local section = options.self.section:match('lualine_(.*)') if section > 'c' then section = section_highlight_map[section] end for _, mode in ipairs(modes) do local highlight_group_name = { options.self.section, highlight_tag, mode } local default_color_table = options.theme[mode] and options.theme[mode][section] or options.theme.normal[section] local bg = (color.bg or default_color_table.bg) local fg = (color.fg or default_color_table.fg) -- 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 and fg ~= normal_hl.fg then highlight(table.concat(highlight_group_name, '_'), fg, bg, color.gui) end else normal_hl = {bg = bg, fg = fg} highlight(table.concat(highlight_group_name, '_'), fg, bg, color.gui) end end return options.self.section..'_'..highlight_tag end -- @description: retrieve highlight_groups for components -- @param highlight_name:(string) highlight group name without mode -- return value of create_component_highlight_group is to be passed in -- this parameter to receive highlight that was created -- @return: (string) formated highlight group name function M.component_format_highlight(highlight_name) local highlight_group = highlight_name if highlight_name:find('no_mode') == #highlight_name - #'no_mode' + 1 then return '%#'..highlight_group..'#' end if vim.g.statusline_winid == vim.fn.win_getid() then highlight_group = append_mode(highlight_group) else highlight_group = highlight_group..'_inactive' end if utils.highlight_exists(highlight_group)then return '%#'..highlight_group..'#' else return '%#'..highlight_name..'_normal#' end end function M.format_highlight(is_focused, highlight_group) if highlight_group > 'lualine_c' then highlight_group = 'lualine_' .. section_highlight_map[highlight_group:match('lualine_(.)')] end local highlight_name = highlight_group if not is_focused then return '%#' .. highlight_group .. [[_inactive#]] else highlight_name = append_mode(highlight_group) end return '%#' .. highlight_name ..'#' end -- @description : Provides transitional highlights for section separators. -- @param left_section_data :(string) section before separator -- @param right_section_data:(string) section after separator -- @param reverse :(string) Whether it's a left separator or right separator -- '▶️' and '◀️' needs reverse colors so this parameter needs to be set true. -- @return: (string) formated highlight group name function M.get_transitional_highlights(left_section_data, right_section_data, reverse ) local left_highlight_name, right_highlight_name -- Grab the last highlighter of left section if left_section_data then -- extract highlight_name from .....%#highlight_name# left_highlight_name = left_section_data:match('.*%%#(.*)#') else -- When right section us unavailable default to lualine_c left_highlight_name = append_mode('lualine_c') if not utils.highlight_exists(left_highlight_name) then left_highlight_name = 'lualine_c_normal' end end if right_section_data then -- using vim-regex cause lua-paterns don't have non-greedy matching -- extract highlight_name from %#highlight_name#.... right_highlight_name = vim.fn.matchlist(right_section_data, [[%#\(.\{-\}\)#]])[2] else -- When right section us unavailable default to lualine_c right_highlight_name = append_mode('lualine_c') if not utils.highlight_exists(right_highlight_name) then right_highlight_name = 'lualine_c_normal' end end -- When both left and right highlights are same nothing to transition to if left_highlight_name == right_highlight_name then return end -- construct the name of hightlight group local highlight_name if left_highlight_name:find('lualine_') == 1 then highlight_name = left_highlight_name .. '_to_' .. right_highlight_name else highlight_name = 'lualine_' .. left_highlight_name .. '_to_' .. right_highlight_name end if not utils.highlight_exists(highlight_name) then -- Create the highlight_group if needed local function set_transitional_highlights() -- Get colors from highlights -- using string.format to convert decimal to hexadecimal local fg = utils.extract_highlight_colors(left_highlight_name, 'guibg') local bg = utils.extract_highlight_colors(right_highlight_name, 'guibg') if not fg then fg = 'none' end if not bg then bg = 'none' end -- swap the bg and fg when reverse is true. As in that case highlight will -- be placed before section if reverse then fg, bg = bg, fg end highlight(highlight_name, fg, bg) end -- Create highlights and setup to survive colorscheme changes set_transitional_highlights() utils.expand_set_theme(set_transitional_highlights) end return '%#' .. highlight_name .. '#' end return M