Skip to content

feat: colored icons for buffers #1405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,9 @@ sections = {
alpha = 'Alpha'
}, -- Shows specific buffer name for that filetype ( { `filetype` = `buffer_name`, ... } )

-- Colors the buffer icons
colored_icons = true,

-- Automatically updates active buffer color to match color of other components (will be overidden if buffers_color is set)
use_mode_colors = false,

Expand Down Expand Up @@ -788,6 +791,9 @@ sections = {

disabled_buftypes = { 'quickfix', 'prompt' }, -- Hide a window if its buffer's type is disabled

-- Colors the window icons
colored_icons = true,

-- Automatically updates active window color to match color of other components (will be overidden if buffers_color is set)
use_mode_colors = false,

Expand Down
115 changes: 83 additions & 32 deletions lua/lualine/components/buffers/buffer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ local modules = require('lualine_require').lazy_require {
---@param opts table
function Buffer:init(opts)
assert(opts.bufnr, 'Cannot create Buffer without bufnr')
self.component = opts.component
self.bufnr = opts.bufnr
self.buf_index = opts.buf_index
self.options = opts.options
Expand All @@ -32,27 +33,54 @@ function Buffer:get_props()
local modified = self.options.show_modified_status and vim.api.nvim_buf_get_option(self.bufnr, 'modified')
self.modified_icon = modified and self.options.symbols.modified or ''
self.alternate_file_icon = self:is_alternate() and self.options.symbols.alternate_file or ''
self.icon = ''
self.icon = { text = '' }

if self.options.icons_enabled then
local dev
local status, _ = pcall(require, 'nvim-web-devicons')
local dev, hl_group

local status, devicons = pcall(require, 'nvim-web-devicons')
if not status then
dev, _ = '', ''
dev, hl_group = '', ''
elseif self.filetype == 'TelescopePrompt' then
dev, _ = require('nvim-web-devicons').get_icon('telescope')
dev, hl_group = devicons.get_icon('telescope')
elseif self.filetype == 'fugitive' then
dev, _ = require('nvim-web-devicons').get_icon('git')
dev, hl_group = devicons.get_icon('git')
elseif self.filetype == 'vimwiki' then
dev, _ = require('nvim-web-devicons').get_icon('markdown')
dev, hl_group = devicons.get_icon('markdown')
elseif self.buftype == 'terminal' then
dev, _ = require('nvim-web-devicons').get_icon('zsh')
dev, hl_group = devicons.get_icon('zsh')
elseif vim.fn.isdirectory(self.file) == 1 then
dev, _ = self.options.symbols.directory, nil
dev, hl_group = self.options.symbols.directory, nil
else
dev, _ = require('nvim-web-devicons').get_icon(self.file, vim.fn.expand('#' .. self.bufnr .. ':e'))
dev, hl_group = devicons.get_icon(vim.fn.fnamemodify(self.file, ":t"), nil, {default=true})
end

if not dev then
return
end

self.icon.text = dev .. ' '

if not self.options.colored_icons then
return
end
if dev then
self.icon = dev .. ' '

local highlight_color = modules.utils.extract_highlight_colors(hl_group, 'fg')
if highlight_color then
local icon_highlight = self.component.icon_hl_cache[highlight_color]
if not icon_highlight then
local active_bg = modules.utils.extract_highlight_colors(self.highlights["active"].name, 'bg')
local inactive_bg = modules.utils.extract_highlight_colors(self.highlights["inactive"].name, 'bg')

icon_highlight = {
active = self.component:create_hl({ fg = highlight_color, bg = active_bg }, hl_group .. '_active'),
inactive = self.component:create_hl({ fg = highlight_color, bg = inactive_bg }, hl_group .. '_inactive')
}

self.component.icon_hl_cache[highlight_color] = icon_highlight
end

self.icon.colors = icon_highlight
end
end
end
Expand All @@ -74,11 +102,11 @@ function Buffer:render()

if self.ellipse then -- show ellipsis
name = '...'
self.len = 3
else
name = self:apply_mode(name)
name, self.len = self:apply_mode(name)
end
name = Buffer.apply_padding(name, self.options.padding)
self.len = vim.fn.strchars(name)
name, self.len = Buffer.apply_padding(name, self.len, self.options.padding)

-- setup for mouse clicks
local line = self:configure_mouse_click(name)
Expand Down Expand Up @@ -151,42 +179,65 @@ function Buffer:name()
end

---adds spaces to left and right
function Buffer.apply_padding(str, padding)
---@return string str
---@return number len displayed length
function Buffer.apply_padding(str, len, padding)
local l_padding, r_padding = 1, 1
if type(padding) == 'number' then
l_padding, r_padding = padding, padding
elseif type(padding) == 'table' then
l_padding, r_padding = padding.left or 0, padding.right or 0
end
return string.rep(' ', l_padding) .. str .. string.rep(' ', r_padding)
return string.rep(' ', l_padding) .. str .. string.rep(' ', r_padding), len + l_padding + r_padding
end

function Buffer:apply_mode(name)
if self.options.mode == 0 then
return string.format('%s%s%s%s', self.alternate_file_icon, self.icon, name, self.modified_icon)
end
---renders icon block, colorizing it if needed
---@return string str
---@return number len length that won't be displayed (color tags)
function Buffer:render_icon()
if self.icon.colors then
local status = self.current and 'active' or 'inactive'

local icon_color = modules.highlight.component_format_highlight(self.icon.colors[status])
local default_color = modules.highlight.component_format_highlight(self.highlights[status])

if self.options.mode == 1 then
return string.format('%s%s %s%s', self.alternate_file_icon, self.buf_index or '', self.icon, self.modified_icon)
local icon = icon_color .. self.icon.text .. default_color
local extra_len = vim.fn.strchars(icon_color) + vim.fn.strchars(default_color)

return icon, extra_len
end

if self.options.mode == 2 then
return string.format(
return self.icon.text, 0
end

---formats buffer string according to selected mode
---@return string str
---@return number len displayed length
function Buffer:apply_mode(name)
local icon, extra_len = self:render_icon()

local str
if self.options.mode == 0 then
str = string.format('%s%s%s%s', self.alternate_file_icon, icon, name, self.modified_icon)
elseif self.options.mode == 1 then
str = string.format('%s%s %s%s', self.alternate_file_icon, self.buf_index or '', icon, self.modified_icon)
elseif self.options.mode == 2 then
str = string.format(
'%s%s %s%s%s',
self.alternate_file_icon,
self.buf_index or '',
self.icon,
icon,
name,
self.modified_icon
)
elseif self.options.mode == 3 then
str = string.format('%s%s %s%s', self.alternate_file_icon, self.bufnr or '', icon, self.modified_icon)
else -- if self.options.mode == 4 then
str = string.format('%s%s %s%s%s', self.alternate_file_icon, self.bufnr or '', icon, name, self.modified_icon)
end

if self.options.mode == 3 then
return string.format('%s%s %s%s', self.alternate_file_icon, self.bufnr or '', self.icon, self.modified_icon)
end

-- if self.options.mode == 4 then
return string.format('%s%s %s%s%s', self.alternate_file_icon, self.bufnr or '', self.icon, name, self.modified_icon)
local len = vim.fn.strchars(str) - extra_len
return str, len
end

return Buffer
3 changes: 3 additions & 0 deletions lua/lualine/components/buffers/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local default_options = {
fzf = 'FZF',
alpha = 'Alpha',
},
colored_icons = true,
use_mode_colors = false,
buffers_color = {
active = nil,
Expand Down Expand Up @@ -61,6 +62,7 @@ function M:init(options)
inactive = get_hl('lualine_' .. options.self.section, false),
}
self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options)
self.icon_hl_cache = {}
if self.options.component_name == 'buffers' then
self.highlights = {
active = self:create_hl(self.options.buffers_color.active, 'active'),
Expand All @@ -73,6 +75,7 @@ function M:new_buffer(bufnr, buf_index)
bufnr = bufnr or vim.api.nvim_get_current_buf()
buf_index = buf_index or ''
return Buffer:new {
component = self,
bufnr = bufnr,
buf_index = buf_index,
options = self.options,
Expand Down
16 changes: 10 additions & 6 deletions lua/lualine/components/windows/window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@ function Window:is_current()
end

function Window:apply_mode(name)
if self.options.mode == 0 then
return string.format('%s%s%s', self.icon, name, self.modified_icon)
end
local icon, extra_len = self:render_icon()

if self.options.mode == 1 then
return string.format('%s %s%s', self.win_number, self.icon, self.modified_icon)
local str
if self.options.mode == 0 then
str = string.format('%s%s%s', icon, name, self.modified_icon)
elseif self.options.mode == 1 then
str = string.format('%s %s%s', self.win_number, icon, self.modified_icon)
else
str = string.format('%s %s%s%s', self.win_number, icon, name, self.modified_icon)
end

return string.format('%s %s%s%s', self.win_number, self.icon, name, self.modified_icon)
local len = vim.fn.strchars(str) - extra_len
return str, len
end

function Window:configure_mouse_click(name)
Expand Down
30 changes: 18 additions & 12 deletions tests/spec/lualine_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -731,14 +731,17 @@ describe('Lualine', function()
tabline:expect([===[
highlights = {
1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true }
2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true }
3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true }
4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true }
2: lualine_a_buffers_DevIconTxt_active = { bg = "#a89984", fg = "#89e051", nocombine = true }
3: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true }
4: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true }
5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true }
}
|{1: 󰈙 a.txt }
{2:}
{3: ... }
{4: }|
|{1: }
{2:󰈙 }
{1:a.txt }
{3:}
{4: ... }
{5: }|
]===])
end)

Expand All @@ -751,12 +754,15 @@ describe('Lualine', function()
tabline:expect([===[
highlights = {
1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true }
2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true }
3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true }
2: lualine_a_buffers_DevIconLua_active = { bg = "#a89984", fg = "#51a0cf", nocombine = true }
3: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true }
4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true }
}
|{1:  t.lua }
{2:}
{3: }|
|{1: }
{2: }
{1:t.lua }
{3:}
{4: }|
]===])
end)

Expand Down