neoconf.nvim/lua/neoconf/settings.lua

167 lines
3.1 KiB
Lua

local util = require("neoconf.util")
local M = {}
---@class Settings
---@field _settings table
---@field file string
local Settings = {}
Settings.__index = Settings
function M.new(settings)
local ret = setmetatable({ _settings = {} }, Settings)
for k, v in pairs(settings or {}) do
ret:set(k, v)
end
return ret
end
function M.expand(tbl)
if type(tbl) ~= "table" then
return tbl
end
local ret = M.new()
for key, value in pairs(tbl) do
ret:set(key, value)
end
return ret:get()
end
function M.path(key)
if not key or key == "" then
return {}
end
if type(key) ~= "string" then
return { key }
end
local parts = {}
for part in string.gmatch(key, "[^.]+") do
table.insert(parts, part)
end
return parts
end
function Settings:clear()
self._settings = {}
end
function Settings:set(key, value)
local parts = M.path(key)
if #parts == 0 then
self._settings = value
return
end
local node = self._settings
for i = 1, #parts - 1, 1 do
local part = parts[i]
if type(node[part]) ~= "table" then
node[part] = {}
end
node = node[part]
end
node[parts[#parts]] = value
end
---@param opts? {defaults?:table, expand?:boolean}
function Settings:get(key, opts)
---@type table|nil
local node = self._settings
for _, part in ipairs(M.path(key)) do
if type(node) ~= "table" then
node = nil
break
end
node = node[part]
end
if opts and opts.expand and type(node) == "table" then
node = M.expand(node)
end
if opts and opts.defaults then
if node == nil then
return vim.deepcopy(opts.defaults)
end
if type(node) ~= "table" then
return node
end
node = util.merge({}, opts.defaults, node)
end
return node
end
function Settings:load(file)
self:clear()
if util.exists(file) then
local data = util.read_file(file)
local ok, json = pcall(util.json_decode, data)
if ok then
for k, v in pairs(M.expand(json)) do
self:set(k, v)
end
else
util.error(("failed to load json settings from %s"):format(file))
end
end
return self
end
---@param settings Settings
---@return Settings
function Settings:merge(settings, key)
if not settings then
return M.new()
end
if settings.__index ~= Settings then
settings = M.new(settings)
end
if key then
local value = util.merge({}, self:get(key) or {}, settings._settings)
self:set(key, value)
else
self._settings = util.merge(self._settings, settings._settings)
end
return self
end
M._cache = {}
function M.clear(fname)
M._cache[fname] = nil
end
function M.get(fname, opts)
opts = opts or {}
fname = util.fqn(fname)
if not M._cache[fname] and util.exists(fname) then
M._cache[fname] = M.new():load(fname)
end
return M._cache[fname]
end
function M.get_local(root_dir)
local settings = M.new()
util.for_each_local(function(file, key)
settings:merge(M.get(file), key)
end, root_dir)
return settings
end
function M.get_global()
local settings = M.new()
util.for_each_global(function(file, key)
settings:merge(M.get(file), key)
end)
return settings
end
return M