feat(picker): added new icons picker for nerd fonts and emoji. Closes #703

This commit is contained in:
Folke Lemaitre 2025-01-23 10:51:17 +01:00
parent 570d2191d5
commit 97898e910d
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
6 changed files with 138 additions and 2 deletions

View file

@ -218,11 +218,19 @@ function M.loclist(picker)
setqflist(items, { win = picker.main })
end
function M.copy(_, item)
function M.yank(_, item)
if item then
vim.fn.setreg("+", item.data or item.text)
end
end
M.copy = M.yank
function M.put(picker, item)
picker:close()
if item then
vim.api.nvim_put({ item.data or item.text }, "c", true, true)
end
end
function M.history_back(picker)
picker:hist()

View file

@ -66,6 +66,11 @@ Snacks.util.set_hl({
GitStatusUntracked = "SnacksPickerGitStatus",
ManSection = "Number",
ManPage = "Special",
-- Icons
Icon = "Special",
IconSource = "@constant",
IconName = "@keyword",
IconCategory = "@module",
-- LSP Symbol Kinds
IconArray = "@punctuation.bracket",
IconBoolean = "@boolean",

View file

@ -272,6 +272,16 @@ M.highlights = {
preview = "preview",
}
---@class snacks.picker.icons.Config: snacks.picker.Config
---@field icons? string[]
M.icons = {
icons = { "nerd_fonts", "emoji" },
finder = "icons",
format = "icon",
layout = { preset = "vscode" },
confirm = "put",
}
M.jumps = {
finder = "vim_jumps",
format = "file",

View file

@ -514,4 +514,19 @@ function M.debug(item, picker)
return ret
end
function M.icon(item, picker)
local a = Snacks.picker.util.align
---@cast item snacks.picker.Icon
local ret = {} ---@type snacks.picker.Highlight[]
ret[#ret + 1] = { a(item.icon, 2), "SnacksPickerIcon" }
ret[#ret + 1] = { " " }
ret[#ret + 1] = { a(item.source, 10), "SnacksPickerIconSource" }
ret[#ret + 1] = { " " }
ret[#ret + 1] = { a(item.name, 30), "SnacksPickerIconName" }
ret[#ret + 1] = { " " }
ret[#ret + 1] = { a(item.category, 8), "SnacksPickerIconCategory" }
return ret
end
return M

View file

@ -0,0 +1,97 @@
local M = {}
---@type table<string, {url: string, v?:number, build: fun(data:table):snacks.picker.Icon[]}>
M.sources = {
nerd_fonts = {
url = "https://github.com/ryanoasis/nerd-fonts/raw/refs/heads/master/glyphnames.json",
v = 3,
build = function(data)
---@cast data table<string, {char:string, code:string}>
local ret = {} ---@type snacks.picker.Icon[]
for name, info in pairs(data) do
if name ~= "METADATA" then
local font, icon = name:match("^([%w_]+)%-(.*)$")
if not font then
error("Invalid icon name: " .. name)
end
table.insert(ret, {
name = icon,
icon = info.char,
source = "nerd font",
category = font,
})
end
end
return ret
end,
},
emoji = {
url = "https://raw.githubusercontent.com/muan/unicode-emoji-json/refs/heads/main/data-by-emoji.json",
v = 4,
build = function(data)
---@cast data table<string, {name:string, slug:string, group:string}>
local ret = {} ---@type snacks.picker.Icon[]
for icon, info in pairs(data) do
table.insert(ret, {
name = info.name,
icon = icon,
source = "emoji",
category = info.group,
})
end
return ret
end,
},
}
---@class snacks.picker.Icon: snacks.picker.finder.Item
---@field icon string
---@field name string
---@field source string
---@field category string
---@field desc? string
---@param source_name string
local function load(source_name)
local source = M.sources[source_name]
local file = vim.fn.stdpath("cache") .. "/snacks/picker/icons/" .. source_name .. "-v" .. (source.v or 1) .. ".json"
vim.fn.mkdir(vim.fn.fnamemodify(file, ":h"), "p")
if vim.fn.filereadable(file) == 1 then
local fd = assert(io.open(file, "r"))
local data = fd:read("*a")
fd:close()
return vim.json.decode(data) ---@type snacks.picker.Icon[]
end
Snacks.notify("Fetching `" .. source_name .. "` icons …")
if vim.fn.executable("curl") == 0 then
Snacks.notify.error("`curl` is required to fetch icons")
return {}
end
local out = vim.fn.system({ "curl", "-s", "-L", source.url })
if vim.v.shell_error ~= 0 then
Snacks.notify.error(out, { title = "Icons Picker" })
return {}
end
local icons = source.build(vim.json.decode(out))
local fd = assert(io.open(file, "w"))
fd:write(vim.json.encode(icons))
fd:close()
return icons
end
---@param opts snacks.picker.icons.Config
---@type snacks.picker.finder
function M.icons(opts)
local ret = {} ---@type snacks.picker.Icon[]
for _, source in ipairs(opts.icons or { "nerd_fonts", "emoji" }) do
vim.list_extend(ret, load(source))
end
for _, icon in ipairs(ret) do
icon.text = Snacks.picker.util.text(icon, { "source", "category", "name" })
icon.data = icon.icon
end
return ret
end
return M

View file

@ -64,10 +64,11 @@ function M.text(item, keys)
return buffer:get()
end
---@param text string
---@param text? string
---@param width number
---@param opts? {align?: "left" | "right" | "center", truncate?: boolean}
function M.align(text, width, opts)
text = text or ""
opts = opts or {}
opts.align = opts.align or "left"
local tw = vim.api.nvim_strwidth(text)