feat(mini-snippets): add support for package.json

This commit is contained in:
Daniel Mata 2025-02-21 19:51:28 -06:00
parent 16a772452a
commit c483ba2406

View file

@ -7,15 +7,38 @@ if lazyvim_docs then
-- https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-snippets.md#expand -- https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-snippets.md#expand
-- :h MiniSnippets-session -- :h MiniSnippets-session
-- NOTE: To add your own snippets, you will need an extension manifest
-- in a runtime directory (e.g. ~/.config/nvim/package.json)
-- See https://github.com/rafamadriz/friendly-snippets/blob/main/package.json
-- for friendly-snippets' manifest
-- See https://code.visualstudio.com/api/references/extension-manifest
-- and https://code.visualstudio.com/api/references/contribution-points#contributes.snippets
-- for the original spec
--
-- To add other snippets to a filetype, add them to opts.filetype_additions
-- To override a lang's filetypes, add an entry to opts.lang_filetypes
-- For example:
--[[
return {
{
"echasnovski/mini.snippets",
opts = {
filetype_additions = { plaintext = { "license", "loremipsum" } },
-- use "plaintext" snippets when context.lang is "text"
lang_filetypes = { text = { "plaintext" } },
},
},
}
--]]
-- Example override for your own config: -- Example override for your own config:
--[[ --[[
return { return {
{ {
"echasnovski/mini.snippets", "echasnovski/mini.snippets",
opts = function(_, opts) opts = function(_, opts)
-- By default, for opts.snippets, the extra for mini.snippets only adds gen_loader.from_lang() -- By default, for opts.snippets, the extra for mini.snippets only adds from_package_json()
-- This provides a sensible quickstart, integrating with friendly-snippets -- This provides a sensible quickstart, integrating with friendly-snippets
-- and your own language-specific snippets
-- --
-- In order to change opts.snippets, replace the entire table inside your own opts -- In order to change opts.snippets, replace the entire table inside your own opts
@ -27,7 +50,7 @@ if lazyvim_docs then
-- Load snippets based on current language by reading files from -- Load snippets based on current language by reading files from
-- "snippets/" subdirectories from 'runtimepath' directories. -- "snippets/" subdirectories from 'runtimepath' directories.
snippets.gen_loader.from_lang(), -- this is the default in the extra... snippets.gen_loader.from_lang(),
} }
end, end,
}, },
@ -66,7 +89,7 @@ return {
"echasnovski/mini.snippets", "echasnovski/mini.snippets",
event = "InsertEnter", -- don't depend on other plugins to load... event = "InsertEnter", -- don't depend on other plugins to load...
dependencies = "rafamadriz/friendly-snippets", dependencies = "rafamadriz/friendly-snippets",
opts = function() config = function(_, opts)
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
LazyVim.cmp.actions.snippet_stop = function() end -- by design, <esc> should not stop the session! LazyVim.cmp.actions.snippet_stop = function() end -- by design, <esc> should not stop the session!
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
@ -75,8 +98,175 @@ return {
end end
local mini_snippets = require("mini.snippets") local mini_snippets = require("mini.snippets")
return {
snippets = { mini_snippets.gen_loader.from_lang() }, local function from_package_json()
local filetype_additions, lang_filetypes = opts.filetype_additions or {}, opts.lang_filetypes or {}
local cache = { filetype_snippets = {}, lang_snippets = {}, paths = nil }
local function get_paths(filetype)
if cache.paths ~= nil then
cache.paths[filetype] = cache.paths[filetype] or {}
return cache.paths[filetype]
end
cache.paths = {}
local function insert_path(lang, root, path)
cache.paths[lang] = cache.paths[lang] or {}
table.insert(cache.paths[lang], vim.fs.joinpath(root, path))
end
local manifests = vim.api.nvim_get_runtime_file("package.json", true)
for _, manifest in ipairs(manifests) do
local function warn_of_manifest(msg)
LazyVim.warn({ "Could not parse", manifest, "", msg }, { title = "mini-snippets extra" })
end
if vim.fn.filereadable(manifest) ~= 1 then
warn_of_manifest("File is not readable")
goto continue
end
local file, err = io.open(manifest)
if file == nil then
warn_of_manifest(err)
goto continue
end
local contents = file:read("*a")
file:close()
local ok, json = pcall(vim.json.decode, contents)
if not ok then
warn_of_manifest(json)
goto continue
end
if type(json) ~= "table" then
warn_of_manifest("Contents should be a table")
goto continue
end
local snippets = vim.tbl_get(json, "contributes", "snippets")
if snippets == nil then
goto continue
end
if type(snippets) ~= "table" then
warn_of_manifest("`snippets` should be a table")
goto continue
end
local i = 0
for _ in pairs(snippets) do
i = i + 1
local snippet = snippets[i]
if snippet == nil or type(snippet) ~= "table" then
warn_of_manifest("`snippets` should be a list of tables")
goto continue
end
if type(snippet.path) ~= "string" then
warn_of_manifest("`path` of snippet #" .. i .. " should be a string")
goto continue
end
local language = snippet.language
if type(language) == "table" then
local j = 0
for _ in pairs(language) do
j = j + 1
if type(language[j]) ~= "string" then
warn_of_manifest(
"`language` of snippet #"
.. i
.. " should be a string, a list of strings, or nil for global snippets"
)
goto continue
end
end
elseif not (type(language) == "string" or language == nil) then
warn_of_manifest(
"`language` of snippet #" .. i .. " should be a string, a list of strings, or nil for global snippets"
)
goto continue
end
end
local root = vim.fs.dirname(manifest)
for _, snippet in ipairs(snippets) do
local language = snippet.language
local path = snippet.path
if type(language) == "table" then
for _, ft in ipairs(language) do
insert_path(ft, root, path)
end
elseif type(language) == "string" then
insert_path(language, root, path)
else
insert_path("global", root, path)
end
end
::continue::
end
cache.paths[filetype] = cache.paths[filetype] or {}
return cache.paths[filetype]
end
local function get_filetype_snippets(filetype)
if type(filetype) == "string" then
if cache.filetype_snippets[filetype] ~= nil then
return cache.filetype_snippets[filetype]
end
local paths = {}
for _, ft in ipairs(filetype_additions[filetype] or {}) do
for _, path in ipairs(get_paths(ft)) do
table.insert(paths, path)
end
end
for _, path in ipairs(get_paths(filetype)) do
table.insert(paths, path)
end
cache.filetype_snippets[filetype] = vim.tbl_map(mini_snippets.read_file, paths)
return cache.filetype_snippets[filetype]
end
return vim.tbl_map(get_filetype_snippets, filetype)
end
local function get_lang_snippets(lang)
cache.lang_snippets[lang] = cache.lang_snippets[lang]
or get_filetype_snippets(lang_filetypes[lang] or { vim.treesitter.language.get_filetypes(lang), "global" })
return cache.lang_snippets[lang]
end
return function(context)
local lang = (context or {}).lang
if type(lang) ~= "string" then
return get_lang_snippets("global")
end
return get_lang_snippets(lang)
end
end
opts = vim.tbl_deep_extend("force", {
snippets = { from_package_json() },
-- Following the behavior of vim.snippets, -- Following the behavior of vim.snippets,
-- the intended usage of <esc> is to be able to temporarily exit into normal mode for quick edits. -- the intended usage of <esc> is to be able to temporarily exit into normal mode for quick edits.
@ -92,7 +282,9 @@ return {
select(snippets, insert) select(snippets, insert)
end, end,
}, },
} }, opts)
mini_snippets.setup(opts)
end, end,
}, },