feat(#1917): add diagnostic highlighting and icon placement (#2396)

* feat(#1917): add renderer.highlight_diagnostics

* feat(#1917): add renderer.highlight_diagnostics

* feat(#1917): add enderer.icons.diagnostics_placement

* feat(#1917): add renderer.icons.show.diagnostics

* feat(#1917): document highlight overrides
This commit is contained in:
Alexander Courtis 2023-09-03 12:29:33 +10:00 committed by GitHub
parent 28c3980b25
commit 323f65cb9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 148 additions and 64 deletions

View File

@ -345,6 +345,7 @@ applying configuration.
add_trailing = false, add_trailing = false,
group_empty = false, group_empty = false,
highlight_git = false, highlight_git = false,
highlight_diagnostics = false,
full_name = false, full_name = false,
highlight_opened_files = "none", highlight_opened_files = "none",
highlight_modified = "none", highlight_modified = "none",
@ -373,6 +374,7 @@ applying configuration.
}, },
}, },
git_placement = "before", git_placement = "before",
diagnostics_placement = "signcolumn",
modified_placement = "after", modified_placement = "after",
padding = " ", padding = " ",
symlink_arrow = " ➛ ", symlink_arrow = " ➛ ",
@ -381,6 +383,7 @@ applying configuration.
folder = true, folder = true,
folder_arrow = true, folder_arrow = true,
git = true, git = true,
diagnostics = true,
modified = true, modified = true,
}, },
glyphs = { glyphs = {
@ -689,15 +692,7 @@ Open a file or directory in your preferred application.
Windows: `{ "/c", "start", '""' }` Windows: `{ "/c", "start", '""' }`
*nvim-tree.diagnostics* *nvim-tree.diagnostics*
Show LSP and COC diagnostics in the signcolumn Show LSP and COC diagnostics.
Note that the modified sign will take precedence over the diagnostics signs.
`NOTE`: it will use the default diagnostic color groups to highlight the signs.
If you wish to customize, you can override these groups:
- `NvimTreeLspDiagnosticsError`
- `NvimTreeLspDiagnosticsWarning`
- `NvimTreeLspDiagnosticsInformation`
- `NvimTreeLspDiagnosticsHint`
*nvim-tree.diagnostics.enable* *nvim-tree.diagnostics.enable*
Enable/disable the feature. Enable/disable the feature.
@ -881,7 +876,7 @@ Window / buffer setup.
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
*nvim-tree.view.signcolumn* *nvim-tree.view.signcolumn*
Show diagnostic sign column. Value can be `"yes"`, `"auto"`, `"no"`. Show |signcolumn|. Value can be `"yes"`, `"auto"`, `"no"`.
Type: `string`, Default: `"yes"` Type: `string`, Default: `"yes"`
*nvim-tree.view.float* *nvim-tree.view.float*
@ -910,6 +905,8 @@ Window / buffer setup.
*nvim-tree.renderer* *nvim-tree.renderer*
UI rendering setup UI rendering setup
Highlight override precedence: git < opened < modified < diagnostics
*nvim-tree.renderer.add_trailing* *nvim-tree.renderer.add_trailing*
Appends a trailing slash to folder names. Appends a trailing slash to folder names.
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
@ -923,11 +920,17 @@ UI rendering setup
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
*nvim-tree.renderer.highlight_git* *nvim-tree.renderer.highlight_git*
Enable file highlight for git attributes using `NvimTreeGit*` highlight groups. Enable highlight for git attributes using `NvimTreeGit*` highlight groups.
Requires |nvim-tree.git.enable| Requires |nvim-tree.git.enable|
This can be used with or without the icons. This can be used with or without the icons.
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
*nvim-tree.renderer.highlight_diagnostics*
Enable highlight for diagnostics using `LspDiagnosticsError*Text` highlight groups.
Requires |nvim-tree.diagnostics.enable|
This can be used with or without the icons.
Type: `boolean`, Default: `false`
*nvim-tree.renderer.highlight_opened_files* *nvim-tree.renderer.highlight_opened_files*
Highlight icons and/or names for |bufloaded()| files using the Highlight icons and/or names for |bufloaded()| files using the
`NvimTreeOpenedFile` highlight group. `NvimTreeOpenedFile` highlight group.
@ -979,6 +982,8 @@ UI rendering setup
*nvim-tree.renderer.icons* *nvim-tree.renderer.icons*
Configuration options for icons. Configuration options for icons.
Sign column icon precedence: git < modified < diagnostics
*nvim-tree.renderer.icons.web_devicons* *nvim-tree.renderer.icons.web_devicons*
Configure optional plugin `"nvim-tree/nvim-web-devicons"` Configure optional plugin `"nvim-tree/nvim-web-devicons"`
@ -1010,9 +1015,14 @@ UI rendering setup
Place where the git icons will be rendered. Place where the git icons will be rendered.
Can be `"after"` or `"before"` filename (after the file/folders icons) Can be `"after"` or `"before"` filename (after the file/folders icons)
or `"signcolumn"` (requires |nvim-tree.view.signcolumn| enabled). or `"signcolumn"` (requires |nvim-tree.view.signcolumn| enabled).
Note that the diagnostic signs and the modified sign will take precedence over the git signs.
Type: `string`, Default: `before` Type: `string`, Default: `before`
*nvim-tree.renderer.icons.diagnostics_placement*
Place where the diagnostics icon will be rendered.
Can be `"after"` or `"before"` filename (after the file/folders icons)
or `"signcolumn"` (requires |nvim-tree.view.signcolumn| enabled).
Type: `string`, Default: `signcolumn`
*nvim-tree.renderer.icons.modified_placement* *nvim-tree.renderer.icons.modified_placement*
Place where the modified icon will be rendered. Place where the modified icon will be rendered.
Can be `"after"` or `"before"` filename (after the file/folders icons) Can be `"after"` or `"before"` filename (after the file/folders icons)
@ -1048,6 +1058,11 @@ UI rendering setup
Requires |git.enable| `= true` Requires |git.enable| `= true`
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.show.diagnostics*
Show a diagnostics status icon, see |renderer.icons.diagnostics_placement|
Requires |diagnostics.enable| `= true`
Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.show.modified* *nvim-tree.renderer.icons.show.modified*
Show a modified icon, see |renderer.icons.modified_placement| Show a modified icon, see |renderer.icons.modified_placement|
Requires |modified.enable| `= true` Requires |modified.enable| `= true`
@ -2079,6 +2094,14 @@ NvimTreeFileNew (NvimTreeGitNew)
NvimTreeFileDeleted (NvimTreeGitDeleted) NvimTreeFileDeleted (NvimTreeGitDeleted)
NvimTreeFileIgnored (NvimTreeGitIgnored) NvimTreeFileIgnored (NvimTreeGitIgnored)
There are also links for text highlight for diagnostic status, linked by
default to their icon equivalent.
NvimTreeLspDiagnosticsErrorText (NvimTreeLspDiagnosticsError)
NvimTreeLspDiagnosticsWarningText (NvimTreeLspDiagnosticsWarning)
NvimTreeLspDiagnosticsInfoText (NvimTreeLspDiagnosticsInformation)
NvimTreeLspDiagnosticsHintText (NvimTreeLspDiagnosticsHint)
There are 2 highlight groups for the live filter feature There are 2 highlight groups for the live filter feature
NvimTreeLiveFilterPrefix NvimTreeLiveFilterPrefix

View File

@ -409,6 +409,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
add_trailing = false, add_trailing = false,
group_empty = false, group_empty = false,
highlight_git = false, highlight_git = false,
highlight_diagnostics = false,
full_name = false, full_name = false,
highlight_opened_files = "none", highlight_opened_files = "none",
highlight_modified = "none", highlight_modified = "none",
@ -437,6 +438,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
}, },
}, },
git_placement = "before", git_placement = "before",
diagnostics_placement = "signcolumn",
modified_placement = "after", modified_placement = "after",
padding = " ", padding = " ",
symlink_arrow = "", symlink_arrow = "",
@ -445,6 +447,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
folder = true, folder = true,
folder_arrow = true, folder_arrow = true,
git = true, git = true,
diagnostics = true,
modified = true, modified = true,
}, },
glyphs = { glyphs = {

View File

@ -81,6 +81,14 @@ local function get_links()
FileStaged = "NvimTreeGitStaged", FileStaged = "NvimTreeGitStaged",
FileDeleted = "NvimTreeGitDeleted", FileDeleted = "NvimTreeGitDeleted",
FileIgnored = "NvimTreeGitIgnored", FileIgnored = "NvimTreeGitIgnored",
LspDiagnosticsError = "DiagnosticError",
LspDiagnosticsWarning = "DiagnosticWarn",
LspDiagnosticsInformation = "DiagnosticInfo",
LspDiagnosticsHint = "DiagnosticHint",
LspDiagnosticsErrorText = "NvimTreeLspDiagnosticsError",
LspDiagnosticsWarningText = "NvimTreeLspDiagnosticsWarning",
LspDiagnosticsInformationText = "NvimTreeLspDiagnosticsInformation",
LspDiagnosticsHintText = "NvimTreeLspDiagnosticsHintFile",
Popup = "Normal", Popup = "Normal",
GitIgnored = "Comment", GitIgnored = "Comment",
StatusLine = "StatusLine", StatusLine = "StatusLine",

View File

@ -5,24 +5,7 @@ local log = require "nvim-tree.log"
local M = {} local M = {}
local GROUP = "NvimTreeDiagnosticSigns"
local severity_levels = { Error = 1, Warning = 2, Information = 3, Hint = 4 } local severity_levels = { Error = 1, Warning = 2, Information = 3, Hint = 4 }
local sign_names = {
{ "NvimTreeSignError", "NvimTreeLspDiagnosticsError" },
{ "NvimTreeSignWarning", "NvimTreeLspDiagnosticsWarning" },
{ "NvimTreeSignInformation", "NvimTreeLspDiagnosticsInformation" },
{ "NvimTreeSignHint", "NvimTreeLspDiagnosticsHint" },
}
local function add_sign(linenr, severity)
local buf = view.get_bufnr()
if not vim.api.nvim_buf_is_valid(buf) or not vim.api.nvim_buf_is_loaded(buf) then
return
end
local sign_name = sign_names[severity][1]
vim.fn.sign_place(0, GROUP, sign_name, buf, { lnum = linenr, priority = 2 })
end
local function from_nvim_lsp() local function from_nvim_lsp()
local buffer_severity = {} local buffer_severity = {}
@ -85,14 +68,6 @@ local function is_using_coc()
return vim.g.coc_service_initialized == 1 return vim.g.coc_service_initialized == 1
end end
function M.clear()
if not M.enable or not view.is_buf_valid(view.get_bufnr()) then
return
end
vim.fn.sign_unplace(GROUP)
end
function M.update() function M.update()
if not M.enable or not core.get_explorer() or not view.is_buf_valid(view.get_bufnr()) then if not M.enable or not core.get_explorer() or not view.is_buf_valid(view.get_bufnr()) then
return return
@ -108,8 +83,6 @@ function M.update()
buffer_severity = from_nvim_lsp() buffer_severity = from_nvim_lsp()
end end
M.clear()
local nodes_by_line = utils.get_nodes_by_line(core.get_explorer().nodes, core.get_nodes_starting_line()) local nodes_by_line = utils.get_nodes_by_line(core.get_explorer().nodes, core.get_nodes_starting_line())
for _, node in pairs(nodes_by_line) do for _, node in pairs(nodes_by_line) do
node.diag_status = nil node.diag_status = nil
@ -129,26 +102,18 @@ function M.update()
then then
log.line("diagnostics", " matched fold node '%s'", node.absolute_path) log.line("diagnostics", " matched fold node '%s'", node.absolute_path)
node.diag_status = severity node.diag_status = severity
add_sign(line, severity)
elseif nodepath == bufpath then elseif nodepath == bufpath then
log.line("diagnostics", " matched file node '%s'", node.absolute_path) log.line("diagnostics", " matched file node '%s'", node.absolute_path)
node.diag_status = severity node.diag_status = severity
add_sign(line, severity)
end end
end end
end end
end end
log.profile_end(profile) log.profile_end(profile)
require("nvim-tree.renderer").draw()
end) end)
end end
local links = {
NvimTreeLspDiagnosticsError = "DiagnosticError",
NvimTreeLspDiagnosticsWarning = "DiagnosticWarn",
NvimTreeLspDiagnosticsInformation = "DiagnosticInfo",
NvimTreeLspDiagnosticsHint = "DiagnosticHint",
}
function M.setup(opts) function M.setup(opts)
M.enable = opts.diagnostics.enable M.enable = opts.diagnostics.enable
M.debounce_delay = opts.diagnostics.debounce_delay M.debounce_delay = opts.diagnostics.debounce_delay
@ -160,14 +125,6 @@ function M.setup(opts)
M.show_on_dirs = opts.diagnostics.show_on_dirs M.show_on_dirs = opts.diagnostics.show_on_dirs
M.show_on_open_dirs = opts.diagnostics.show_on_open_dirs M.show_on_open_dirs = opts.diagnostics.show_on_open_dirs
vim.fn.sign_define(sign_names[1][1], { text = opts.diagnostics.icons.error, texthl = sign_names[1][2] })
vim.fn.sign_define(sign_names[2][1], { text = opts.diagnostics.icons.warning, texthl = sign_names[2][2] })
vim.fn.sign_define(sign_names[3][1], { text = opts.diagnostics.icons.info, texthl = sign_names[3][2] })
vim.fn.sign_define(sign_names[4][1], { text = opts.diagnostics.icons.hint, texthl = sign_names[4][2] })
for lhs, rhs in pairs(links) do
vim.cmd("hi def link " .. lhs .. " " .. rhs)
end
end end
return M return M

View File

@ -5,6 +5,7 @@ local git = require "nvim-tree.renderer.components.git"
local pad = require "nvim-tree.renderer.components.padding" local pad = require "nvim-tree.renderer.components.padding"
local icons = require "nvim-tree.renderer.components.icons" local icons = require "nvim-tree.renderer.components.icons"
local modified = require "nvim-tree.renderer.components.modified" local modified = require "nvim-tree.renderer.components.modified"
local diagnostics = require "nvim-tree.renderer.components.diagnostics"
local Builder = {} local Builder = {}
Builder.__index = Builder Builder.__index = Builder
@ -72,6 +73,14 @@ function Builder:configure_git_icons_placement(where)
return self return self
end end
function Builder:configure_diagnostics_icon_placement(where)
if where ~= "after" and where ~= "before" and where ~= "signcolumn" then
where = "before" -- default before
end
self.diagnostics_placement = where
return self
end
function Builder:configure_modified_placement(where) function Builder:configure_modified_placement(where)
if where ~= "after" and where ~= "before" and where ~= "signcolumn" then if where ~= "after" and where ~= "before" and where ~= "signcolumn" then
where = "after" -- default after where = "after" -- default after
@ -206,20 +215,29 @@ end
function Builder:_get_git_icons(node) function Builder:_get_git_icons(node)
local git_icons = git.get_icons(node) local git_icons = git.get_icons(node)
if git_icons and #git_icons > 0 and self.git_placement == "signcolumn" then if git_icons and #git_icons > 0 and self.git_placement == "signcolumn" then
local sign = git_icons[1] table.insert(self.signs, { sign = git_icons[1].hl, lnum = self.index + 1, priority = 1 })
table.insert(self.signs, { sign = sign.hl, lnum = self.index + 1, priority = 1 })
git_icons = nil git_icons = nil
end end
return git_icons return git_icons
end end
---@param node table
---@return HighlightedString[]|nil icon
function Builder:_get_diagnostics_icon(node)
local diagnostics_icon = diagnostics.get_icon(node)
if diagnostics_icon and self.diagnostics_placement == "signcolumn" then
table.insert(self.signs, { sign = diagnostics_icon.hl, lnum = self.index + 1, priority = 2 })
diagnostics_icon = nil
end
return diagnostics_icon
end
---@param node table ---@param node table
---@return HighlightedString|nil icon ---@return HighlightedString|nil icon
function Builder:_get_modified_icon(node) function Builder:_get_modified_icon(node)
local modified_icon = modified.get_icon(node) local modified_icon = modified.get_icon(node)
if modified_icon and self.modified_placement == "signcolumn" then if modified_icon and self.modified_placement == "signcolumn" then
local sign = modified_icon table.insert(self.signs, { sign = modified_icon.hl, lnum = self.index + 1, priority = 3 })
table.insert(self.signs, { sign = sign.hl, lnum = self.index + 1, priority = 3 })
modified_icon = nil modified_icon = nil
end end
return modified_icon return modified_icon
@ -263,6 +281,12 @@ function Builder:_get_highlight_override(node, unloaded_bufnr)
end end
end end
-- diagnostic status
local diagnostic_highlight = diagnostics.get_highlight(node)
if diagnostic_highlight then
name_hl = diagnostic_highlight
end
return icon_hl, name_hl return icon_hl, name_hl
end end
@ -270,9 +294,10 @@ end
---@param icon HighlightedString ---@param icon HighlightedString
---@param name HighlightedString ---@param name HighlightedString
---@param git_icons HighlightedString[]|nil ---@param git_icons HighlightedString[]|nil
---@param diagnostics_icon HighlightedString|nil
---@param modified_icon HighlightedString|nil ---@param modified_icon HighlightedString|nil
---@return HighlightedString[] ---@return HighlightedString[]
function Builder:_format_line(padding, icon, name, git_icons, modified_icon) function Builder:_format_line(padding, icon, name, git_icons, diagnostics_icon, modified_icon)
local added_len = 0 local added_len = 0
local function add_to_end(t1, t2) local function add_to_end(t1, t2)
for _, v in ipairs(t2) do for _, v in ipairs(t2) do
@ -298,13 +323,21 @@ function Builder:_format_line(padding, icon, name, git_icons, modified_icon)
if modified_icon and self.modified_placement == "before" then if modified_icon and self.modified_placement == "before" then
add_to_end(line, { modified_icon }) add_to_end(line, { modified_icon })
end end
if diagnostics_icon and self.diagnostics_placement == "before" then
add_to_end(line, { diagnostics_icon })
end
add_to_end(line, { name }) add_to_end(line, { name })
if git_icons and self.git_placement == "after" then if git_icons and self.git_placement == "after" then
add_to_end(line, git_icons) add_to_end(line, git_icons)
end end
if modified_icon and self.modified_placement == "after" then if modified_icon and self.modified_placement == "after" then
add_to_end(line, { modified_icon }) add_to_end(line, { modified_icon })
end end
if diagnostics_icon and self.diagnostics_placement == "after" then
add_to_end(line, { diagnostics_icon })
end
return line return line
end end
@ -314,6 +347,7 @@ function Builder:_build_line(node, idx, num_children, unloaded_bufnr)
local padding = pad.get_padding(self.depth, idx, num_children, node, self.markers) local padding = pad.get_padding(self.depth, idx, num_children, node, self.markers)
local git_icons = self:_get_git_icons(node) local git_icons = self:_get_git_icons(node)
local modified_icon = self:_get_modified_icon(node) local modified_icon = self:_get_modified_icon(node)
local diagnostics_icon = self:_get_diagnostics_icon(node)
-- main components -- main components
local is_folder = node.nodes ~= nil local is_folder = node.nodes ~= nil
@ -336,7 +370,7 @@ function Builder:_build_line(node, idx, num_children, unloaded_bufnr)
name.hl = name_hl name.hl = name_hl
end end
local line = self:_format_line(padding, icon, name, git_icons, modified_icon) local line = self:_format_line(padding, icon, name, git_icons, diagnostics_icon, modified_icon)
self:_insert_line(self:_unwrap_highlighted_strings(line)) self:_insert_line(self:_unwrap_highlighted_strings(line))
self.index = self.index + 1 self.index = self.index + 1

View File

@ -0,0 +1,58 @@
local M = {}
local H = {}
local I = {}
---diagnostics text highlight group if there is a status
---@param node table
---@return string|nil highlight
function M.get_highlight(node)
if M.config.diagnostics.enable and M.config.renderer.highlight_diagnostics then
return H[node.diag_status]
end
end
---diagnostics icon if there is a status
---@param node table
---@return HighlightedString|nil modified icon
function M.get_icon(node)
if M.config.diagnostics.enable and M.config.renderer.icons.show.diagnostics then
return I[node.diag_status]
end
end
function M.setup(opts)
M.config = {
diagnostics = opts.diagnostics,
renderer = opts.renderer,
}
H[vim.diagnostic.severity.ERROR] = "NvimTreeLspDiagnosticsErrorText"
H[vim.diagnostic.severity.WARN] = "NvimTreeLspDiagnosticsWarningText"
H[vim.diagnostic.severity.INFO] = "NvimTreeLspDiagnosticsInfoText"
H[vim.diagnostic.severity.HINT] = "NvimTreeLspDiagnosticsHintText"
I[vim.diagnostic.severity.ERROR] = {
str = M.config.diagnostics.icons.error,
hl = "NvimTreeLspDiagnosticsError",
}
I[vim.diagnostic.severity.WARN] = {
str = M.config.diagnostics.icons.warning,
hl = "NvimTreeLspDiagnosticsWarning",
}
I[vim.diagnostic.severity.INFO] = {
str = M.config.diagnostics.icons.info,
hl = "NvimTreeLspDiagnosticsInfo",
}
I[vim.diagnostic.severity.HINT] = {
str = M.config.diagnostics.icons.hint,
hl = "NvimTreeLspDiagnosticsHint",
}
for _, i in ipairs(I) do
vim.fn.sign_define(i.hl, { text = i.str, texthl = i.hl })
end
end
return M

View File

@ -1,5 +1,4 @@
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local diagnostics = require "nvim-tree.diagnostics"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local view = require "nvim-tree.view" local view = require "nvim-tree.view"
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
@ -9,6 +8,7 @@ local _padding = require "nvim-tree.renderer.components.padding"
local icon_component = require "nvim-tree.renderer.components.icons" local icon_component = require "nvim-tree.renderer.components.icons"
local full_name = require "nvim-tree.renderer.components.full-name" local full_name = require "nvim-tree.renderer.components.full-name"
local git = require "nvim-tree.renderer.components.git" local git = require "nvim-tree.renderer.components.git"
local diagnostics = require "nvim-tree.renderer.components.diagnostics"
local Builder = require "nvim-tree.renderer.builder" local Builder = require "nvim-tree.renderer.builder"
local live_filter = require "nvim-tree.live-filter" local live_filter = require "nvim-tree.live-filter"
local marks = require "nvim-tree.marks" local marks = require "nvim-tree.marks"
@ -69,6 +69,7 @@ function M.draw(unloaded_bufnr)
:configure_modified_highlighting(M.config.highlight_modified) :configure_modified_highlighting(M.config.highlight_modified)
:configure_icon_padding(M.config.icons.padding) :configure_icon_padding(M.config.icons.padding)
:configure_git_icons_placement(M.config.icons.git_placement) :configure_git_icons_placement(M.config.icons.git_placement)
:configure_diagnostics_icon_placement(M.config.icons.diagnostics_placement)
:configure_modified_placement(M.config.icons.modified_placement) :configure_modified_placement(M.config.icons.modified_placement)
:configure_symlink_destination(M.config.symlink_destination) :configure_symlink_destination(M.config.symlink_destination)
:configure_filter(live_filter.filter, live_filter.prefix) :configure_filter(live_filter.filter, live_filter.prefix)
@ -84,7 +85,6 @@ function M.draw(unloaded_bufnr)
vim.api.nvim_win_set_cursor(view.get_winnr(), cursor) vim.api.nvim_win_set_cursor(view.get_winnr(), cursor)
end end
diagnostics.update()
marks.draw() marks.draw()
view.grow_from_content() view.grow_from_content()
@ -102,6 +102,7 @@ function M.setup(opts)
full_name.setup(opts) full_name.setup(opts)
git.setup(opts) git.setup(opts)
modified.setup(opts) modified.setup(opts)
diagnostics.setup(opts)
icon_component.setup(opts) icon_component.setup(opts)
end end