mirror of
https://github.com/folke/snacks.nvim
synced 2025-08-05 11:18:26 +00:00
feat(picker): added new icons
picker for nerd fonts and emoji. Closes #703
This commit is contained in:
parent
570d2191d5
commit
97898e910d
6 changed files with 138 additions and 2 deletions
|
@ -218,11 +218,19 @@ function M.loclist(picker)
|
||||||
setqflist(items, { win = picker.main })
|
setqflist(items, { win = picker.main })
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.copy(_, item)
|
function M.yank(_, item)
|
||||||
if item then
|
if item then
|
||||||
vim.fn.setreg("+", item.data or item.text)
|
vim.fn.setreg("+", item.data or item.text)
|
||||||
end
|
end
|
||||||
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)
|
function M.history_back(picker)
|
||||||
picker:hist()
|
picker:hist()
|
||||||
|
|
|
@ -66,6 +66,11 @@ Snacks.util.set_hl({
|
||||||
GitStatusUntracked = "SnacksPickerGitStatus",
|
GitStatusUntracked = "SnacksPickerGitStatus",
|
||||||
ManSection = "Number",
|
ManSection = "Number",
|
||||||
ManPage = "Special",
|
ManPage = "Special",
|
||||||
|
-- Icons
|
||||||
|
Icon = "Special",
|
||||||
|
IconSource = "@constant",
|
||||||
|
IconName = "@keyword",
|
||||||
|
IconCategory = "@module",
|
||||||
-- LSP Symbol Kinds
|
-- LSP Symbol Kinds
|
||||||
IconArray = "@punctuation.bracket",
|
IconArray = "@punctuation.bracket",
|
||||||
IconBoolean = "@boolean",
|
IconBoolean = "@boolean",
|
||||||
|
|
|
@ -272,6 +272,16 @@ M.highlights = {
|
||||||
preview = "preview",
|
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 = {
|
M.jumps = {
|
||||||
finder = "vim_jumps",
|
finder = "vim_jumps",
|
||||||
format = "file",
|
format = "file",
|
||||||
|
|
|
@ -514,4 +514,19 @@ function M.debug(item, picker)
|
||||||
return ret
|
return ret
|
||||||
end
|
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
|
return M
|
||||||
|
|
97
lua/snacks/picker/source/icons.lua
Normal file
97
lua/snacks/picker/source/icons.lua
Normal 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
|
|
@ -64,10 +64,11 @@ function M.text(item, keys)
|
||||||
return buffer:get()
|
return buffer:get()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param text string
|
---@param text? string
|
||||||
---@param width number
|
---@param width number
|
||||||
---@param opts? {align?: "left" | "right" | "center", truncate?: boolean}
|
---@param opts? {align?: "left" | "right" | "center", truncate?: boolean}
|
||||||
function M.align(text, width, opts)
|
function M.align(text, width, opts)
|
||||||
|
text = text or ""
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
opts.align = opts.align or "left"
|
opts.align = opts.align or "left"
|
||||||
local tw = vim.api.nvim_strwidth(text)
|
local tw = vim.api.nvim_strwidth(text)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue