lk.Settings

:dump ()

local lib = {} lib.__index = lib lk.Settings = lib local private = {}

setmetatable(lib, { __call = function(lib, mod_name, defaults) local self, path = private.loadSettings(mod_name) defaults.save = lib.save defaults._module = {name = mod_name, path = path} defaults.__index = defaults private.prepareTables(self, defaults) return setmetatable(self, defaults) end })

--- Dump settings as a lua file in the settings path. if later then -- save in a few ms (do not repeatedly save during window -- move/resize) local thread = self._module.thread if thread then thread:kill() end self._module.thread = lk.Thread(function() self._module.thread = nil self:save() end, elapsed() + 500) else local path = self._module.path lk.writeall(path, lib.dump(self)) end end

return string.format('return %s\n', private.dump(self, ' ', true)) end

--=============================================== PRIVATE function private.loadSettings(mod_name) local path = string.format('%s/.lubyk/%s.lua', os.getenv('HOME'), mod_name) local code = loadfile(path) local self if code then self = code() else self = {} end return self, path end

local function dump(o, indent, is_root) if type(o) == 'table' then if o._placeholder then -- ignore return nil end local first = true local s = '{' for k,v in pairs(o) do if is_root and k == '_module' then -- ignore else local v = dump(v, indent .. ' ') if v then if first then s = s .. '\n' first = false end if type(k) == 'number' then s = s .. indent .. '[' .. k .. ']' elseif k:match('^[a-zA-Z_]+$') then s = s .. indent .. k else s = s .. indent .. '["'.. k .. '"]' end s = s .. ' = ' .. v .. ',\n' end end end return s .. '}' elseif type(o) == 'number' or type(o) == 'boolean' then return tostring(o) elseif type(o) == 'string' then return '"' .. o:gsub('"', '\\"') .. '"' else -- IGNORE end end

private.dump = dump

-- Copy on write local cow = {}

function cow.__index(tbl, key) return rawget(tbl._placeholder, key) end

function cow.__newindex(tbl, key, value) -- make a copy of defaults local copy = tbl._self[tbl._key] if copy == tbl then copy = {} for k, v in pairs(tbl._placeholder) do copy[k] = v end copy[key] = value -- remove placeholder and work on the copy alone from now tbl._self[tbl._key] = copy else copy[key] = value end end

function private:prepareTables(defaults) -- This is to enable natural value setting for tables -- editor.Setting.foo.bar = 'baz' -- <-- this should copy table defaults for k, v in pairs(defaults) do if type(v) == 'table' and not rawget(self, k) then self[k] = setmetatable({_placeholder = v, _self = self, _key = k}, cow) end end end