From afcee9c85a89c98d3725525c2bb371dbd87ee27b Mon Sep 17 00:00:00 2001 From: Maikel Date: Fri, 12 Dec 2025 15:19:31 +0100 Subject: [PATCH] feat(icons): embed minimal nvim-web-devicons subset + compat shim --- lua/volt/compat/devicons.lua | 30 +++++++++++ lua/volt/icons.lua | 101 +++++++++++++++++++++++++++++++++++ lua/volt/icons/full.lua | 34 ++++++++++++ lua/volt/init.lua | 11 ++++ scripts/extract_icons.lua | 65 ++++++++++++++++++++++ 5 files changed, 241 insertions(+) create mode 100644 lua/volt/compat/devicons.lua create mode 100644 lua/volt/icons.lua create mode 100644 lua/volt/icons/full.lua create mode 100644 scripts/extract_icons.lua diff --git a/lua/volt/compat/devicons.lua b/lua/volt/compat/devicons.lua new file mode 100644 index 0000000..382d771 --- /dev/null +++ b/lua/volt/compat/devicons.lua @@ -0,0 +1,30 @@ +-- lua/volt/compat/devicons.lua +-- Backwards compatibility shim that mimics the minimal nvim-web-devicons API + +local icons = require "volt.icons" + +local M = {} + +-- Mimic nvim-web-devicons.get_icon(filename, extension, opts) +function M.get_icon(name, ext, opts) + local icon, color, hl_name = icons.get_icon(name, ext) + local hl = "VoltIcon" .. (hl_name or "Default") + return icon, hl +end + +-- Minimal get_icon_color(filename, extension) +function M.get_icon_color(name, ext, opts) + local _, color = icons.get_icon(name, ext) + return color +end + +-- Provide a filetype-based helper (best-effort) +function M.get_icon_by_filetype(ft, opts) + return M.get_icon(ft, ft, opts) +end + +function M.setup(opts) + -- noop: Volt manages icons internally +end + +return M diff --git a/lua/volt/icons.lua b/lua/volt/icons.lua new file mode 100644 index 0000000..4492287 --- /dev/null +++ b/lua/volt/icons.lua @@ -0,0 +1,101 @@ +-- lua/volt/icons.lua +-- Minimal, cache-friendly icon provider with optional lazy full-set loader + +local M = {} + +-- Icon cache for performance +local icon_cache = {} + +-- Lightweight icon data (commonly used) +local icons_by_extension = { + lua = { icon = "", color = "#51A0CF", name = "Lua" }, + js = { icon = "", color = "#EAD41C", name = "JavaScript" }, + ts = { icon = "", color = "#2b7489", name = "TypeScript" }, + py = { icon = "", color = "#3572A5", name = "Python" }, + md = { icon = "", color = "#083fa1", name = "Markdown" }, + rs = { icon = "", color = "#dea584", name = "Rust" }, + go = { icon = "", color = "#00ADD8", name = "Go" }, + toml= { icon = "", color = "#9c4221", name = "TOML" }, + json= { icon = "ﬥ", color = "#cbcb41", name = "JSON" }, + sh = { icon = "", color = "#6e4a7e", name = "Shell" }, +} + +local icons_by_filename = { + [".gitignore"] = { icon = "", color = "#F54D27", name = "GitIgnore" }, + ["Makefile"] = { icon = "", color = "#6D8086", name = "Makefile" }, + ["Dockerfile"] = { icon = "", color = "#0db7ed", name = "Dockerfile" }, + ["README.md"] = { icon = "", color = "#083fa1", name = "Readme" }, + ["LICENSE"] = { icon = "", color = "#6d8086", name = "License" }, + ["package.json"]= { icon = "", color = "#cbcb41", name = "Npm" }, + ["tsconfig.json"]= { icon = "", color = "#2b7489", name = "TSConfig" }, + ["Cargo.toml"] = { icon = "", color = "#dea584", name = "Cargo" }, + [".env"] = { icon = "", color = "#4f5d95", name = "Env" }, + ["init.lua"] = { icon = "", color = "#51A0CF", name = "LuaInit" }, +} + +-- Default fallback icon +local default_icon = { + icon = "", + color = "#6d8086", + name = "Default" +} + +function M.get_icon(name, ext) + local cache_key = (name or "") .. ":" .. (ext or "") + if icon_cache[cache_key] then + return unpack(icon_cache[cache_key]) + end + + local icon_data + + -- filename match (case-insensitive) + if name then + local lname = name:lower() + icon_data = icons_by_filename[lname] + end + + -- extension match + if not icon_data and ext and ext ~= "" then + local lext = ext:lower() + icon_data = icons_by_extension[lext] + end + + icon_data = icon_data or default_icon + + icon_cache[cache_key] = { icon_data.icon, icon_data.color, icon_data.name } + return icon_data.icon, icon_data.color, icon_data.name +end + +-- Lazy loading for full icon set +function M.load_full_icons() + if M.full_icons_loaded then return end + + local ok, full_icons = pcall(require, "volt.icons.full") + if ok and full_icons then + if full_icons.by_extension then + icons_by_extension = vim.tbl_extend("force", icons_by_extension, full_icons.by_extension) + end + if full_icons.by_filename then + icons_by_filename = vim.tbl_extend("force", icons_by_filename, full_icons.by_filename) + end + -- clear cache so new icons are picked up + icon_cache = {} + end + + M.full_icons_loaded = true +end + +-- Utility to get icon with highlight group +function M.get_icon_with_hl(name, ext) + local icon, color, hl_name = M.get_icon(name, ext) + if color then + local safe_name = (hl_name or "Default"):gsub("%s+", "") + local hl_group = "VoltIcon" .. safe_name + -- set highlight (idempotent) + pcall(vim.api.nvim_set_hl, 0, hl_group, { fg = color }) + return icon, hl_group + end + return icon, nil +end + +return M diff --git a/lua/volt/icons/full.lua b/lua/volt/icons/full.lua new file mode 100644 index 0000000..dfc4d85 --- /dev/null +++ b/lua/volt/icons/full.lua @@ -0,0 +1,34 @@ +-- lua/volt/icons/full.lua +-- Extracted subset of the full nvim-web-devicons dataset. +-- Only a representative subset is included here to keep file short. +-- The extraction script (scripts/extract_icons.lua) can be used to generate +-- a complete table from an installed nvim-web-devicons. + +return { + by_extension = { + lua = { icon = "", color = "#51A0CF", name = "Lua" }, + js = { icon = "", color = "#EAD41C", name = "JavaScript" }, + ts = { icon = "", color = "#2b7489", name = "TypeScript" }, + py = { icon = "", color = "#3572A5", name = "Python" }, + rs = { icon = "", color = "#dea584", name = "Rust" }, + go = { icon = "", color = "#00ADD8", name = "Go" }, + md = { icon = "", color = "#083fa1", name = "Markdown" }, + json= { icon = "ﬥ", color = "#cbcb41", name = "JSON" }, + sh = { icon = "", color = "#6e4a7e", name = "Shell" }, + toml= { icon = "", color = "#9c4221", name = "TOML" }, + -- Note: full dataset would continue here... + }, + by_filename = { + [".gitignore"] = { icon = "", color = "#F54D27", name = "GitIgnore" }, + ["Makefile"] = { icon = "", color = "#6D8086", name = "Makefile" }, + ["Dockerfile"] = { icon = "", color = "#0db7ed", name = "Dockerfile" }, + ["README.md"] = { icon = "", color = "#083fa1", name = "Readme" }, + ["LICENSE"] = { icon = "", color = "#6d8086", name = "License" }, + ["package.json"]= { icon = "", color = "#cbcb41", name = "Npm" }, + ["tsconfig.json"]= { icon = "", color = "#2b7489", name = "TSConfig" }, + ["Cargo.toml"] = { icon = "", color = "#dea584", name = "Cargo" }, + [".env"] = { icon = "", color = "#4f5d95", name = "Env" }, + ["init.lua"] = { icon = "", color = "#51A0CF", name = "LuaInit" }, + -- Note: full dataset would continue here... + }, +} diff --git a/lua/volt/init.lua b/lua/volt/init.lua index 2f75ea6..0315863 100644 --- a/lua/volt/init.lua +++ b/lua/volt/init.lua @@ -1,9 +1,11 @@ +-- Updated: expose icons API for Volt and provide helper M.get_file_icon local M = {} local api = vim.api local map = vim.keymap.set local draw = require "volt.draw" local state = require "volt.state" local utils = require "volt.utils" +local icons = require "volt.icons" -- new local get_section = function(tb, name) for _, value in ipairs(tb) do @@ -139,4 +141,13 @@ M.close = function(buf) end) end +-- Expose icon utilities (new) +M.icons = icons + +M.get_file_icon = function(filepath) + local name = vim.fn.fnamemodify(filepath, ":t") + local ext = vim.fn.fnamemodify(filepath, ":e") + return M.icons.get_icon_with_hl(name, ext) +end + return M diff --git a/scripts/extract_icons.lua b/scripts/extract_icons.lua new file mode 100644 index 0000000..d869a5f --- /dev/null +++ b/scripts/extract_icons.lua @@ -0,0 +1,65 @@ +-- scripts/extract_icons.lua +-- Helper to extract the full icon tables from an installed nvim-web-devicons +-- Usage (from repo root): +-- nvim --headless -c 'lua require("scripts.extract_icons").run()' -c qa +-- +-- This will write lua/volt/icons/full.lua. Requires nvim-web-devicons to be installed +-- in your runtimepath. + +local M = {} + +local function safe_write(path, content) + local f, err = io.open(path, "w") + if not f then + return nil, err + end + f:write(content) + f:close() + return true +end + +function M.run() + local ok, devicons = pcall(require, "nvim-web-devicons") + if not ok or not devicons then + return error("nvim-web-devicons not found in runtimepath") + end + + -- Try to get maps from the plugin API or fall back to known internals + local by_ext = {} + local by_name = {} + + -- Attempt to use plugin-provided functions if available + if devicons.get_icons then + -- some versions expose a combined table + local tbl = devicons.get_icons() + if tbl.by_extension then by_ext = tbl.by_extension end + if tbl.by_filename then by_name = tbl.by_filename end + else + -- try common functions / internals + if devicons.get_icon_by_filetype then + -- Not standard; skip + end + -- Try reading exported tables (different plugin versions vary) + by_ext = devicons.icons_by_file_extension or devicons.get_icons_by_extension and devicons.get_icons_by_extension() or by_ext + by_name = devicons.icons_by_filename or devicons.get_icons_by_filename and devicons.get_icons_by_filename() or by_name + end + + -- Fallback: if those calls returned functions/unknown, ensure final tables + by_ext = by_ext or {} + by_name = by_name or {} + + local content = {} + table.insert(content, "-- Auto-generated by scripts/extract_icons.lua") + table.insert(content, "return {") + table.insert(content, " by_extension = " .. vim.inspect(by_ext) .. ",") + table.insert(content, " by_filename = " .. vim.inspect(by_name) .. ",") + table.insert(content, "}") + + local ok, err = safe_write("lua/volt/icons/full.lua", table.concat(content, "\n")) + if not ok then + error("Failed to write lua/volt/icons/full.lua: " .. tostring(err)) + end + print("Wrote lua/volt/icons/full.lua") +end + +return M