write to toml & gitsafe
This commit is contained in:
parent
b2a118b2b3
commit
fa3b27f661
155
lua/timer.lua
155
lua/timer.lua
@ -1,30 +1,161 @@
|
|||||||
local augroup = vim.api.nvim_create_augroup("ScratchBuffer", { clear = true })
|
local TOML = require("toml")
|
||||||
|
|
||||||
|
local augroup = vim.api.nvim_create_augroup("Timer", { clear = true })
|
||||||
|
local focus_events = {}
|
||||||
|
local clean_to = 1
|
||||||
|
|
||||||
|
function trim(s)
|
||||||
|
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clean_from(from, events)
|
||||||
|
local to = 1
|
||||||
|
local clean_events = {}
|
||||||
|
|
||||||
|
-- coppy until from
|
||||||
|
for i = 1, from - 1, 1 do
|
||||||
|
table.insert(clean_events, events[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
local last = {}
|
||||||
|
for i = from, #events, 1
|
||||||
|
do
|
||||||
|
local event = events[i]
|
||||||
|
local next_event = events[i + 1]
|
||||||
|
|
||||||
|
|
||||||
|
if next_event == nil then
|
||||||
|
--do nothing
|
||||||
|
elseif next_event.event == "gain" and event.event == "lose" then
|
||||||
|
if os.difftime(next_event.time, event.time) > 5 * 60 then
|
||||||
|
table.insert(clean_events, event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if last == nil then
|
||||||
|
-- do nothing
|
||||||
|
elseif last.event == "lose" and event.event == "gain" then
|
||||||
|
if os.difftime(event.time, last.time) > 5 * 60 then
|
||||||
|
table.insert(clean_events, event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
last = event
|
||||||
|
end
|
||||||
|
|
||||||
|
return clean_events
|
||||||
|
end
|
||||||
|
|
||||||
|
local function time()
|
||||||
|
focus_events = clean_from(clean_to + 1, focus_events)
|
||||||
|
clean_to = #focus_events
|
||||||
|
|
||||||
|
local t = 0
|
||||||
|
|
||||||
|
local start = os.time()
|
||||||
|
for _, fe in pairs(focus_events) do
|
||||||
|
if fe.event == "gain" then
|
||||||
|
start = fe.time
|
||||||
|
else
|
||||||
|
t = t + os.difftime(fe.time, start)
|
||||||
|
end
|
||||||
|
if focus_events[#focus_events].event == "gain" then
|
||||||
|
t = t + os.difftime(os.time(), focus_events[#focus_events].time)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function log_time()
|
||||||
|
print(time())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function log_time_table()
|
||||||
|
for i, e in pairs(focus_events) do
|
||||||
|
print(i, e.event, os.date("%H:%M:%S", e.time))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function focus_gained()
|
local function focus_gained()
|
||||||
print("Huiii Fokus")
|
print("Huiii Fokus")
|
||||||
|
|
||||||
|
table.insert(focus_events, {
|
||||||
|
time = os.time(),
|
||||||
|
event = "gain",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function focus_lost()
|
local function focus_lost()
|
||||||
print("Hokus pokus fort ist der fokus")
|
print("Hokus pokus fort ist der fokus")
|
||||||
|
|
||||||
|
table.insert(focus_events, {
|
||||||
|
time = os.time(),
|
||||||
|
event = "lose",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function main()
|
local function persist()
|
||||||
print("Hello from our plugin")
|
local filename = ".timer.toml"
|
||||||
print(os.time())
|
local username = trim(os.getenv("USER")) .. "_at_" .. trim(io.popen("hostname -s"):read("*a"))
|
||||||
|
local today = os.date("%y-%m-%d", os.time())
|
||||||
|
local table = {}
|
||||||
|
|
||||||
|
local file = io.open(filename, "r")
|
||||||
|
|
||||||
|
-- if file exists read
|
||||||
|
if file ~= nil then
|
||||||
|
table = TOML.parse(file:read("*a"))
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- write time to table
|
||||||
|
if table[today] ~= nil and table[today][username] ~= nil then
|
||||||
|
table[today][username] = table[today][username] + time()
|
||||||
|
else
|
||||||
|
if table[today] == nil then
|
||||||
|
table[today] = {}
|
||||||
|
end
|
||||||
|
table[today][username] = time()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- write table to file
|
||||||
|
file = io.open(filename, "w")
|
||||||
|
if file == nil then
|
||||||
|
error("can't write to .timer file")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
file:write("## dates and their corresponding seconds been here :)\n", TOML.encode(table), "\n")
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
--reset timer
|
||||||
|
clean_to = 1
|
||||||
|
focus_events = {}
|
||||||
|
focus_gained()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function setup()
|
local function setup()
|
||||||
vim.api.nvim_create_autocmd("VimEnter",
|
vim.api.nvim_create_autocmd("VimEnter",
|
||||||
{ group = augroup, desc = "Set a fennel scratch buffer on load", once = true, callback = main })
|
{ group = augroup, desc = "Start Timer", once = true, callback = focus_gained })
|
||||||
|
vim.api.nvim_create_autocmd("ExitPre",
|
||||||
|
{ group = augroup, desc = "Persist timer", once = true, callback = persist })
|
||||||
|
vim.api.nvim_create_autocmd("FocusGained", { group = augroup, callback = focus_gained })
|
||||||
|
vim.api.nvim_create_autocmd("FocusLost", { group = augroup, callback = focus_lost })
|
||||||
|
|
||||||
vim.api.nvim_create_user_command(
|
vim.api.nvim_create_user_command(
|
||||||
'Timer', -- The name of the command (accessible via :TimerStart)
|
'Timer', -- The name of the command (accessible via :TimerStart)
|
||||||
function()
|
log_time,
|
||||||
print("Timer started!") -- The function executed when the command is called
|
{ nargs = 0 } -- No arguments for this command (you can configure this later)
|
||||||
end,
|
)
|
||||||
{ nargs = 0 } -- No arguments for this command (you can configure this later)
|
vim.api.nvim_create_user_command(
|
||||||
|
'TimerLog', -- The name of the command (accessible via :TimerStart)
|
||||||
|
log_time_table,
|
||||||
|
{ nargs = 0 } -- No arguments for this command (you can configure this later)
|
||||||
|
)
|
||||||
|
vim.api.nvim_create_user_command(
|
||||||
|
'TimerSave', -- The name of the command (accessible via :TimerStart)
|
||||||
|
persist,
|
||||||
|
{ nargs = 0 } -- No arguments for this command (you can configure this later)
|
||||||
)
|
)
|
||||||
vim.api.nvim_create_autocmd("FocusGained", { callback = focus_gained })
|
|
||||||
vim.api.nvim_create_autocmd("FocusLost", { callback = focus_lost })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return { setup = setup }
|
return { setup = setup }
|
||||||
|
644
lua/toml.lua
Normal file
644
lua/toml.lua
Normal file
@ -0,0 +1,644 @@
|
|||||||
|
-- source: https://github.com/jonstoler/lua-toml/blob/master/toml.lua
|
||||||
|
local TOML = {
|
||||||
|
-- denotes the current supported TOML version
|
||||||
|
version = 0.40,
|
||||||
|
|
||||||
|
-- sets whether the parser should follow the TOML spec strictly
|
||||||
|
-- currently, no errors are thrown for the following rules if strictness is turned off:
|
||||||
|
-- tables having mixed keys
|
||||||
|
-- redefining a table
|
||||||
|
-- redefining a key within a table
|
||||||
|
strict = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- converts TOML data into a lua table
|
||||||
|
TOML.parse = function(toml, options)
|
||||||
|
options = options or {}
|
||||||
|
local strict = (options.strict ~= nil and options.strict or TOML.strict)
|
||||||
|
|
||||||
|
-- the official TOML definition of whitespace
|
||||||
|
local ws = "[\009\032]"
|
||||||
|
|
||||||
|
-- the official TOML definition of newline
|
||||||
|
local nl = "[\10"
|
||||||
|
do
|
||||||
|
local crlf = "\13\10"
|
||||||
|
nl = nl .. crlf
|
||||||
|
end
|
||||||
|
nl = nl .. "]"
|
||||||
|
|
||||||
|
-- stores text data
|
||||||
|
local buffer = ""
|
||||||
|
|
||||||
|
-- the current location within the string to parse
|
||||||
|
local cursor = 1
|
||||||
|
|
||||||
|
-- the output table
|
||||||
|
local out = {}
|
||||||
|
|
||||||
|
-- the current table to write to
|
||||||
|
local obj = out
|
||||||
|
|
||||||
|
-- returns the next n characters from the current position
|
||||||
|
local function char(n)
|
||||||
|
n = n or 0
|
||||||
|
return toml:sub(cursor + n, cursor + n)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- moves the current position forward n (default: 1) characters
|
||||||
|
local function step(n)
|
||||||
|
n = n or 1
|
||||||
|
cursor = cursor + n
|
||||||
|
end
|
||||||
|
|
||||||
|
-- move forward until the next non-whitespace character
|
||||||
|
local function skipWhitespace()
|
||||||
|
while (char():match(ws)) do
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- remove the (Lua) whitespace at the beginning and end of a string
|
||||||
|
local function trim(str)
|
||||||
|
return str:gsub("^%s*(.-)%s*$", "%1")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- divide a string into a table around a delimiter
|
||||||
|
local function split(str, delim)
|
||||||
|
if str == "" then return {} end
|
||||||
|
local result = {}
|
||||||
|
local append = delim
|
||||||
|
if delim:match("%%") then
|
||||||
|
append = delim:gsub("%%", "")
|
||||||
|
end
|
||||||
|
for match in (str .. append):gmatch("(.-)" .. delim) do
|
||||||
|
table.insert(result, match)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- produce a parsing error message
|
||||||
|
-- the error contains the line number of the current position
|
||||||
|
local function err(message, strictOnly)
|
||||||
|
if not strictOnly or (strictOnly and strict) then
|
||||||
|
local line = 1
|
||||||
|
local c = 0
|
||||||
|
for l in toml:gmatch("(.-)" .. nl) do
|
||||||
|
c = c + l:len()
|
||||||
|
if c >= cursor then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
line = line + 1
|
||||||
|
end
|
||||||
|
error("TOML: " .. message .. " on line " .. line .. ".", 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- prevent infinite loops by checking whether the cursor is
|
||||||
|
-- at the end of the document or not
|
||||||
|
local function bounds()
|
||||||
|
return cursor <= toml:len()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseString()
|
||||||
|
local quoteType = char() -- should be single or double quote
|
||||||
|
|
||||||
|
-- this is a multiline string if the next 2 characters match
|
||||||
|
local multiline = (char(1) == char(2) and char(1) == char())
|
||||||
|
|
||||||
|
-- buffer to hold the string
|
||||||
|
local str = ""
|
||||||
|
|
||||||
|
-- skip the quotes
|
||||||
|
step(multiline and 3 or 1)
|
||||||
|
|
||||||
|
while (bounds()) do
|
||||||
|
if multiline and char():match(nl) and str == "" then
|
||||||
|
-- skip line break line at the beginning of multiline string
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- keep going until we encounter the quote character again
|
||||||
|
if char() == quoteType then
|
||||||
|
if multiline then
|
||||||
|
if char(1) == char(2) and char(1) == quoteType then
|
||||||
|
step(3)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
else
|
||||||
|
step()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if char():match(nl) and not multiline then
|
||||||
|
err("Single-line string cannot contain line break")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we're in a double-quoted string, watch for escape characters!
|
||||||
|
if quoteType == '"' and char() == "\\" then
|
||||||
|
if multiline and char(1):match(nl) then
|
||||||
|
-- skip until first non-whitespace character
|
||||||
|
step(1) -- go past the line break
|
||||||
|
while (bounds()) do
|
||||||
|
if not char():match(ws) and not char():match(nl) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- all available escape characters
|
||||||
|
local escape = {
|
||||||
|
b = "\b",
|
||||||
|
t = "\t",
|
||||||
|
n = "\n",
|
||||||
|
f = "\f",
|
||||||
|
r = "\r",
|
||||||
|
['"'] = '"',
|
||||||
|
["\\"] = "\\",
|
||||||
|
}
|
||||||
|
-- utf function from http://stackoverflow.com/a/26071044
|
||||||
|
-- converts \uXXX into actual unicode
|
||||||
|
local function utf(char)
|
||||||
|
local bytemarkers = { { 0x7ff, 192 }, { 0xffff, 224 }, { 0x1fffff, 240 } }
|
||||||
|
if char < 128 then return string.char(char) end
|
||||||
|
local charbytes = {}
|
||||||
|
for bytes, vals in pairs(bytemarkers) do
|
||||||
|
if char <= vals[1] then
|
||||||
|
for b = bytes + 1, 2, -1 do
|
||||||
|
local mod = char % 64
|
||||||
|
char = (char - mod) / 64
|
||||||
|
charbytes[b] = string.char(128 + mod)
|
||||||
|
end
|
||||||
|
charbytes[1] = string.char(vals[2] + char)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table.concat(charbytes)
|
||||||
|
end
|
||||||
|
|
||||||
|
if escape[char(1)] then
|
||||||
|
-- normal escape
|
||||||
|
str = str .. escape[char(1)]
|
||||||
|
step(2) -- go past backslash and the character
|
||||||
|
elseif char(1) == "u" then
|
||||||
|
-- utf-16
|
||||||
|
step()
|
||||||
|
local uni = char(1) .. char(2) .. char(3) .. char(4)
|
||||||
|
step(5)
|
||||||
|
uni = tonumber(uni, 16)
|
||||||
|
if (uni >= 0 and uni <= 0xd7ff) and not (uni >= 0xe000 and uni <= 0x10ffff) then
|
||||||
|
str = str .. utf(uni)
|
||||||
|
else
|
||||||
|
err("Unicode escape is not a Unicode scalar")
|
||||||
|
end
|
||||||
|
elseif char(1) == "U" then
|
||||||
|
-- utf-32
|
||||||
|
step()
|
||||||
|
local uni = char(1) .. char(2) .. char(3) .. char(4) .. char(5) .. char(6) .. char(7) .. char(8)
|
||||||
|
step(9)
|
||||||
|
uni = tonumber(uni, 16)
|
||||||
|
if (uni >= 0 and uni <= 0xd7ff) and not (uni >= 0xe000 and uni <= 0x10ffff) then
|
||||||
|
str = str .. utf(uni)
|
||||||
|
else
|
||||||
|
err("Unicode escape is not a Unicode scalar")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
err("Invalid escape")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- if we're not in a double-quoted string, just append it to our buffer raw and keep going
|
||||||
|
str = str .. char()
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return { value = str, type = "string" }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseNumber()
|
||||||
|
local num = ""
|
||||||
|
local exp
|
||||||
|
local date = false
|
||||||
|
while (bounds()) do
|
||||||
|
if char():match("[%+%-%.eE_0-9]") then
|
||||||
|
if not exp then
|
||||||
|
if char():lower() == "e" then
|
||||||
|
-- as soon as we reach e or E, start appending to exponent buffer instead of
|
||||||
|
-- number buffer
|
||||||
|
exp = ""
|
||||||
|
elseif char() ~= "_" then
|
||||||
|
num = num .. char()
|
||||||
|
end
|
||||||
|
elseif char():match("[%+%-0-9]") then
|
||||||
|
exp = exp .. char()
|
||||||
|
else
|
||||||
|
err("Invalid exponent")
|
||||||
|
end
|
||||||
|
elseif char():match(ws) or char() == "#" or char():match(nl) or char() == "," or char() == "]" or char() == "}" then
|
||||||
|
break
|
||||||
|
elseif char() == "T" or char() == "Z" then
|
||||||
|
-- parse the date (as a string, since lua has no date object)
|
||||||
|
date = true
|
||||||
|
while (bounds()) do
|
||||||
|
if char() == "," or char() == "]" or char() == "#" or char():match(nl) or char():match(ws) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
num = num .. char()
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
err("Invalid number")
|
||||||
|
end
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
|
||||||
|
if date then
|
||||||
|
return { value = num, type = "date" }
|
||||||
|
end
|
||||||
|
|
||||||
|
local float = false
|
||||||
|
if num:match("%.") then float = true end
|
||||||
|
|
||||||
|
exp = exp and tonumber(exp) or 0
|
||||||
|
num = tonumber(num)
|
||||||
|
|
||||||
|
if not float then
|
||||||
|
return {
|
||||||
|
-- lua will automatically convert the result
|
||||||
|
-- of a power operation to a float, so we have
|
||||||
|
-- to convert it back to an int with math.floor
|
||||||
|
value = math.floor(num * 10 ^ exp),
|
||||||
|
type = "int",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return { value = num * 10 ^ exp, type = "float" }
|
||||||
|
end
|
||||||
|
|
||||||
|
local parseArray, getValue
|
||||||
|
|
||||||
|
function parseArray()
|
||||||
|
step() -- skip [
|
||||||
|
skipWhitespace()
|
||||||
|
|
||||||
|
local arrayType
|
||||||
|
local array = {}
|
||||||
|
|
||||||
|
while (bounds()) do
|
||||||
|
if char() == "]" then
|
||||||
|
break
|
||||||
|
elseif char():match(nl) then
|
||||||
|
-- skip
|
||||||
|
step()
|
||||||
|
skipWhitespace()
|
||||||
|
elseif char() == "#" then
|
||||||
|
while (bounds() and not char():match(nl)) do
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- get the next object in the array
|
||||||
|
local v = getValue()
|
||||||
|
if not v then break end
|
||||||
|
|
||||||
|
-- set the type if it hasn't been set before
|
||||||
|
if arrayType == nil then
|
||||||
|
arrayType = v.type
|
||||||
|
elseif arrayType ~= v.type then
|
||||||
|
err("Mixed types in array", true)
|
||||||
|
end
|
||||||
|
|
||||||
|
array = array or {}
|
||||||
|
table.insert(array, v.value)
|
||||||
|
|
||||||
|
if char() == "," then
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
skipWhitespace()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
step()
|
||||||
|
|
||||||
|
return { value = array, type = "array" }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseInlineTable()
|
||||||
|
step() -- skip opening brace
|
||||||
|
|
||||||
|
local buffer = ""
|
||||||
|
local quoted = false
|
||||||
|
local tbl = {}
|
||||||
|
|
||||||
|
while bounds() do
|
||||||
|
if char() == "}" then
|
||||||
|
break
|
||||||
|
elseif char() == "'" or char() == '"' then
|
||||||
|
buffer = parseString().value
|
||||||
|
quoted = true
|
||||||
|
elseif char() == "=" then
|
||||||
|
if not quoted then
|
||||||
|
buffer = trim(buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
step() -- skip =
|
||||||
|
skipWhitespace()
|
||||||
|
|
||||||
|
if char():match(nl) then
|
||||||
|
err("Newline in inline table")
|
||||||
|
end
|
||||||
|
|
||||||
|
local v = getValue().value
|
||||||
|
tbl[buffer] = v
|
||||||
|
|
||||||
|
skipWhitespace()
|
||||||
|
|
||||||
|
if char() == "," then
|
||||||
|
step()
|
||||||
|
elseif char():match(nl) then
|
||||||
|
err("Newline in inline table")
|
||||||
|
end
|
||||||
|
|
||||||
|
quoted = false
|
||||||
|
buffer = ""
|
||||||
|
else
|
||||||
|
buffer = buffer .. char()
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
step() -- skip closing brace
|
||||||
|
|
||||||
|
return { value = tbl, type = "array" }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseBoolean()
|
||||||
|
local v
|
||||||
|
if toml:sub(cursor, cursor + 3) == "true" then
|
||||||
|
step(4)
|
||||||
|
v = { value = true, type = "boolean" }
|
||||||
|
elseif toml:sub(cursor, cursor + 4) == "false" then
|
||||||
|
step(5)
|
||||||
|
v = { value = false, type = "boolean" }
|
||||||
|
else
|
||||||
|
err("Invalid primitive")
|
||||||
|
end
|
||||||
|
|
||||||
|
skipWhitespace()
|
||||||
|
if char() == "#" then
|
||||||
|
while (not char():match(nl)) do
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
|
||||||
|
-- figure out the type and get the next value in the document
|
||||||
|
function getValue()
|
||||||
|
if char() == '"' or char() == "'" then
|
||||||
|
return parseString()
|
||||||
|
elseif char():match("[%+%-0-9]") then
|
||||||
|
return parseNumber()
|
||||||
|
elseif char() == "[" then
|
||||||
|
return parseArray()
|
||||||
|
elseif char() == "{" then
|
||||||
|
return parseInlineTable()
|
||||||
|
else
|
||||||
|
return parseBoolean()
|
||||||
|
end
|
||||||
|
-- date regex (for possible future support):
|
||||||
|
-- %d%d%d%d%-[0-1][0-9]%-[0-3][0-9]T[0-2][0-9]%:[0-6][0-9]%:[0-6][0-9][Z%:%+%-%.0-9]*
|
||||||
|
end
|
||||||
|
|
||||||
|
-- track whether the current key was quoted or not
|
||||||
|
local quotedKey = false
|
||||||
|
|
||||||
|
-- parse the document!
|
||||||
|
while (cursor <= toml:len()) do
|
||||||
|
-- skip comments and whitespace
|
||||||
|
if char() == "#" then
|
||||||
|
while (not char():match(nl)) do
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if char():match(nl) then
|
||||||
|
-- skip
|
||||||
|
end
|
||||||
|
|
||||||
|
if char() == "=" then
|
||||||
|
step()
|
||||||
|
skipWhitespace()
|
||||||
|
|
||||||
|
-- trim key name
|
||||||
|
buffer = trim(buffer)
|
||||||
|
|
||||||
|
if buffer:match("^[0-9]*$") and not quotedKey then
|
||||||
|
buffer = tonumber(buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
if buffer == "" and not quotedKey then
|
||||||
|
err("Empty key name")
|
||||||
|
end
|
||||||
|
|
||||||
|
local v = getValue()
|
||||||
|
if v then
|
||||||
|
-- if the key already exists in the current object, throw an error
|
||||||
|
if obj[buffer] then
|
||||||
|
err('Cannot redefine key "' .. buffer .. '"', true)
|
||||||
|
end
|
||||||
|
obj[buffer] = v.value
|
||||||
|
end
|
||||||
|
|
||||||
|
-- clear the buffer
|
||||||
|
buffer = ""
|
||||||
|
quotedKey = false
|
||||||
|
|
||||||
|
-- skip whitespace and comments
|
||||||
|
skipWhitespace()
|
||||||
|
if char() == "#" then
|
||||||
|
while (bounds() and not char():match(nl)) do
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if there is anything left on this line after parsing a key and its value,
|
||||||
|
-- throw an error
|
||||||
|
if not char():match(nl) and cursor < toml:len() then
|
||||||
|
err("Invalid primitive")
|
||||||
|
end
|
||||||
|
elseif char() == "[" then
|
||||||
|
buffer = ""
|
||||||
|
step()
|
||||||
|
local tableArray = false
|
||||||
|
|
||||||
|
-- if there are two brackets in a row, it's a table array!
|
||||||
|
if char() == "[" then
|
||||||
|
tableArray = true
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
|
||||||
|
obj = out
|
||||||
|
|
||||||
|
local function processKey(isLast)
|
||||||
|
isLast = isLast or false
|
||||||
|
buffer = trim(buffer)
|
||||||
|
|
||||||
|
if not quotedKey and buffer == "" then
|
||||||
|
err("Empty table name")
|
||||||
|
end
|
||||||
|
|
||||||
|
if isLast and obj[buffer] and not tableArray and #obj[buffer] > 0 then
|
||||||
|
err("Cannot redefine table", true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set obj to the appropriate table so we can start
|
||||||
|
-- filling it with values!
|
||||||
|
if tableArray then
|
||||||
|
-- push onto cache
|
||||||
|
if obj[buffer] then
|
||||||
|
obj = obj[buffer]
|
||||||
|
if isLast then
|
||||||
|
table.insert(obj, {})
|
||||||
|
end
|
||||||
|
obj = obj[#obj]
|
||||||
|
else
|
||||||
|
obj[buffer] = {}
|
||||||
|
obj = obj[buffer]
|
||||||
|
if isLast then
|
||||||
|
table.insert(obj, {})
|
||||||
|
obj = obj[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
obj[buffer] = obj[buffer] or {}
|
||||||
|
obj = obj[buffer]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while (bounds()) do
|
||||||
|
if char() == "]" then
|
||||||
|
if tableArray then
|
||||||
|
if char(1) ~= "]" then
|
||||||
|
err("Mismatching brackets")
|
||||||
|
else
|
||||||
|
step() -- skip inside bracket
|
||||||
|
end
|
||||||
|
end
|
||||||
|
step() -- skip outside bracket
|
||||||
|
|
||||||
|
processKey(true)
|
||||||
|
buffer = ""
|
||||||
|
break
|
||||||
|
elseif char() == '"' or char() == "'" then
|
||||||
|
buffer = parseString().value
|
||||||
|
quotedKey = true
|
||||||
|
elseif char() == "." then
|
||||||
|
step() -- skip period
|
||||||
|
processKey()
|
||||||
|
buffer = ""
|
||||||
|
else
|
||||||
|
buffer = buffer .. char()
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
buffer = ""
|
||||||
|
quotedKey = false
|
||||||
|
elseif (char() == '"' or char() == "'") then
|
||||||
|
-- quoted key
|
||||||
|
buffer = parseString().value
|
||||||
|
quotedKey = true
|
||||||
|
end
|
||||||
|
|
||||||
|
buffer = buffer .. (char():match(nl) and "" or char())
|
||||||
|
step()
|
||||||
|
end
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
TOML.encode = function(tbl)
|
||||||
|
local toml = ""
|
||||||
|
|
||||||
|
local cache = {}
|
||||||
|
|
||||||
|
local function parse(tbl)
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
if type(v) == "boolean" then
|
||||||
|
toml = toml .. k .. " = " .. tostring(v) .. "\n"
|
||||||
|
elseif type(v) == "number" then
|
||||||
|
toml = toml .. k .. " = " .. tostring(v) .. "\n"
|
||||||
|
elseif type(v) == "string" then
|
||||||
|
local quote = '"'
|
||||||
|
v = v:gsub("\\", "\\\\")
|
||||||
|
|
||||||
|
-- if the string has any line breaks, make it multiline
|
||||||
|
if v:match("^\n(.*)$") then
|
||||||
|
quote = quote:rep(3)
|
||||||
|
v = "\\n" .. v
|
||||||
|
elseif v:match("\n") then
|
||||||
|
quote = quote:rep(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
v = v:gsub("\b", "\\b")
|
||||||
|
v = v:gsub("\t", "\\t")
|
||||||
|
v = v:gsub("\f", "\\f")
|
||||||
|
v = v:gsub("\r", "\\r")
|
||||||
|
v = v:gsub('"', '\\"')
|
||||||
|
v = v:gsub("/", "\\/")
|
||||||
|
toml = toml .. k .. " = " .. quote .. v .. quote .. "\n"
|
||||||
|
elseif type(v) == "table" then
|
||||||
|
local array, arrayTable = true, true
|
||||||
|
local first = {}
|
||||||
|
for kk, vv in pairs(v) do
|
||||||
|
if type(kk) ~= "number" then array = false end
|
||||||
|
if type(vv) ~= "table" then
|
||||||
|
v[kk] = nil
|
||||||
|
first[kk] = vv
|
||||||
|
arrayTable = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if array then
|
||||||
|
if arrayTable then
|
||||||
|
-- double bracket syntax go!
|
||||||
|
table.insert(cache, k)
|
||||||
|
for kk, vv in pairs(v) do
|
||||||
|
toml = toml .. "[[" .. table.concat(cache, ".") .. "]]\n"
|
||||||
|
for k3, v3 in pairs(vv) do
|
||||||
|
if type(v3) ~= "table" then
|
||||||
|
vv[k3] = nil
|
||||||
|
first[k3] = v3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
parse(first)
|
||||||
|
parse(vv)
|
||||||
|
end
|
||||||
|
table.remove(cache)
|
||||||
|
else
|
||||||
|
-- plain ol boring array
|
||||||
|
toml = toml .. k .. " = [\n"
|
||||||
|
for kk, vv in pairs(first) do
|
||||||
|
toml = toml .. tostring(vv) .. ",\n"
|
||||||
|
end
|
||||||
|
toml = toml .. "]\n"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- just a key/value table, folks
|
||||||
|
table.insert(cache, k)
|
||||||
|
toml = toml .. "[" .. table.concat(cache, ".") .. "]\n"
|
||||||
|
parse(first)
|
||||||
|
parse(v)
|
||||||
|
table.remove(cache)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
parse(tbl)
|
||||||
|
|
||||||
|
return toml:sub(1, -2)
|
||||||
|
end
|
||||||
|
|
||||||
|
return TOML
|
Loading…
Reference in New Issue
Block a user