1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-23 13:10:04 +08:00

feat(toml): add data.toml api

This commit is contained in:
wsdjeg 2023-07-31 18:38:40 +08:00 committed by Eric Wong
parent 193be7387f
commit 142ab33223
2 changed files with 392 additions and 0 deletions

View File

@ -0,0 +1,337 @@
--=============================================================================
-- toml.lua --- toml lua api
-- Copyright (c) 2016-2023 Wang Shidong & Contributors
-- Author: Wang Shidong < wsdjeg@outlook.com >
-- URL: https://spacevim.org
-- License: GPLv3
--=============================================================================
local M = {}
local skip_pattern = '\\C^\\%(\\_s\\+\\|#[^\r\n]*\\)'
local table_name_pattern = [[\%([^ [:tab:]#.[\]=]\+\)]]
local table_key_pattern = table_name_pattern
function M.parse(text)
local input = {
text = text,
p = 0,
length = vim.fn.strlen(text),
}
return M._parse(input)
end
function M.parse_file(filename)
if vim.fn.filereadable(filename) == 0 then
error('toml API: No such file ' .. filename)
end
local text = table.concat(vim.fn.readfile(filename), '\n')
return M.parse(vim.fn.iconv(text, 'utf8', vim.o.encoding))
end
function M._skip(input)
while M._match(input, [[\%(\_s\|#\)]]) do
input.p = vim.fn.matchend(input.text, skip_pattern, input.p)
end
end
local regex_prefix = ''
if vim.fn.exists('+regexpengine') == 1 then
regex_prefix = '\\%#=1\\C^'
else
regex_prefix = '\\C^'
end
function M._consume(input, pattern)
M._skip(input)
local _end = vim.fn.matchend(input.text, regex_prefix .. pattern, input.p)
if _end == -1 then
M._error(input)
elseif _end == input.p then
return ''
end
local matched = vim.fn.strpart(input.text, input.p, _end - input.p)
input.p = _end
return matched
end
function M._match(input, pattern)
return vim.fn.match(input.text, regex_prefix .. pattern, input.p) ~= -1
end
function M._eof(input)
return input.p >= input.length
end
function M._error(input)
local buf = {}
local offset = 0
-- @todo !=# string to lua
while (input.p + offset) < input.length and input.text[input.p + offset] ~= '[\r\n]' do
table.insert(buf, input.text[input.p + offset])
offset = offset + 1
end
end
function M._parse(input)
local data = {}
M._skip(input)
while not M._eof(input) do
if M._match(input, '[^ [:tab:]#.[\\]]') then
local key = M._key(input)
M._equals(input)
local value = M._value(input)
M._put_dict(data, key, value)
elseif M._match(input, '\\[\\[') then
local key, value = M._array_of_tables(input)
M._put_array(data, key, value)
elseif M._match(input, '\\[') then
local key, value = M._table(input)
M._put_dict(data, key, value)
else
M._error(input)
end
M._skip(input)
end
return data
end
function M._key(input)
local s = M._consume(input, table_key_pattern)
return s
end
function M._equals(input)
M._consume(input, '=')
return '='
end
function M._value(input)
M._skip(input)
if M._match(input, '"\\{3}') then
return M._multiline_basic_string(input)
elseif M._match(input, '"\\{1}') then
return M._basic_string(input)
elseif M._match(input, "'\\{3}") then
return M._multiline_literal(input)
elseif M._match(input, "'\\{1}") then
return M._literal(input)
elseif M._match(input, '\\[') then
return M._array(input)
elseif M._match(input, '\\%(true\\|false\\)') then
return M._boolean(input)
elseif M._match(input, '\\d\\{4}-') then
return M._datetime(input)
elseif M._match(input, '[+-]\\?\\%(\\d\\+\\.\\d\\|\\d\\+\\%(\\.\\d\\+\\)\\?[eE]\\)') then
return M._float(input)
else
return M._integer(input)
end
end
function M._basic_string(input)
local s = M._consume(input, [["\%(\\"\|[^"]\)*"]])
s = string.sub(s, 2, #s - 1)
return M._unescape(s)
end
function M._multiline_basic_string(input)
local s = M._consume(input, '"\\{3}\\_.\\{-}"\\{3}')
s = string.sub(s, 4, #s - 3)
s = vim.fn.substitute(s, '^\n', '', '')
s = vim.fn.substitute(s, '\\\\' .. '\n' .. '\\_s*', '', 'g')
return M._unescape(s)
end
function M._literal(input)
local s = M._consume(input, "'[^']*'")
return string.sub(s, 2, #s - 1)
end
function M._multiline_literal(input)
local s = M._consume(input, [['\{3}.\{-}'\{3}]])
s = string.sub(s, 4, #s - 3)
s = vim.fn.substitute(s, '^\n', '', '')
return s
end
function M._integer(input)
local s = M._consume(input, '[+-]\\?\\d\\+')
return vim.fn.str2nr(s)
end
function M._float(input)
if M._match(input, [[[+-]\?[0-9.]\+[eE][+-]\?\d\+]]) then
return M._exponent(input)
else
return M._fractional(input)
end
end
function M._fractional(input)
local s = M._consume(input, [[[+-]\?[0-9.]\+]])
return vim.fn.str2float(s)
end
function M._exponent(input)
local s = M._consume(input, [[[+-]\?[0-9.]\+[eE][+-]\?\d\+]])
return vim.fn.str2float(s)
end
function M._boolean(input)
local s = M._consume(input, [[\%(true\|false\)]])
if s == 'true' then
return true
else
return false
end
end
function M._datetime(input)
local s = M._consume(
input,
[[\d\{4}-\d\{2}-\d\{2}T\d\{2}:\d\{2}:\d\{2}\%(Z\|-\?\d\{2}:\d\{2}\|\.\d\+-\d\{2}:\d\{2}\)]]
)
return s
end
function M._array(input)
local ary = {}
M._consume(input, '\\[')
M._skip(input)
while not M._eof(input) and not M._match(input, '\\]') do
table.insert(ary, M._value(input))
M._consume(input, ',\\?')
M._skip(input)
end
M._consume(input, '\\]')
return ary
end
function M._table(input)
local tbl = {}
local name = M._consume(
input,
'\\[\\s*' .. table_name_pattern .. '\\%(\\s*\\.\\s*' .. table_name_pattern .. '\\)*\\s*\\]'
)
name = string.sub(name, 2, #name - 1)
M._skip(input)
while not M._eof(input) and not M._match(input, '\\[') do
local key = M._key(input)
M._equals(input)
local value = M._value(input)
tbl[key] = value
M._skip(input)
end
return name, tbl
end
function M._array_of_tables(input)
local tbl = {}
local name = M._consume(
input,
'\\[\\[\\s*'
.. table_name_pattern
.. '\\%(\\s*\\.\\s*'
.. table_name_pattern
.. '\\)*\\s*\\]\\]'
)
name = string.sub(name, 3, #name - 2)
M._skip(input)
while not M._eof(input) and not M._match(input, '\\[') do
local key = M._key(input)
M._equals(input)
local value = M._value(input)
tbl[key] = value
M._skip(input)
end
return name, tbl
end
function M._unescape(text)
text = vim.fn.substitute(text, '\\\\"', '"', 'g')
text = vim.fn.substitute(text, '\\\\b', '\b', 'g')
text = vim.fn.substitute(text, '\\\\t', '\t', 'g')
text = vim.fn.substitute(text, '\\\\n', '\n', 'g')
text = vim.fn.substitute(text, '\\\\f', '\f', 'g')
text = vim.fn.substitute(text, '\\\\r', '\r', 'g')
text = vim.fn.substitute(text, '\\\\/', '/', 'g')
text = vim.fn.substitute(text, '\\\\\\\\', '\\', 'g')
text = vim.fn.substitute(text, '\\C\\\\u\\(\\x\\{4}\\)', '\\=s:_nr2char("0x" . submatch(1))', 'g')
text = vim.fn.substitute(text, '\\C\\U\\(\\x\\{8}\\)', '\\=s:_nr2char("0x" . submatch(1))', 'g')
return text
end
function M._nr2char(nr)
return vim.fn.iconv(vim.fn.nr2char(nr), vim.o.encoding, 'utf8')
end
local function is_list(t)
return vim.tbl_islist(t)
end
local function is_table(t)
return vim.fn.type(t) == 4
end
local function has_key(t, k)
if type(t) == 'table' and t[k] ~= nil then
return true
else
return false
end
end
function M._put_dict(dict, key_name, value)
local keys = vim.split(key_name, '%.')
local ref = dict
local i = 1
for _, key in ipairs(keys) do
if i == #keys then
break
end
if has_key(ref, key) and is_table(ref[key]) then
ref = ref[key]
elseif has_key(ref, key) and is_list(ref[key]) then
ref = ref[key][#ref[key]]
else
ref[key] = {}
ref = ref[key]
end
i = i + 1
end
ref[keys[#keys]] = value
end
function M._put_array(dict, key_name, value)
local keys = vim.split(key_name, '%.')
local ref = dict
local i = 1
for _, key in ipairs(keys) do
if i == #keys then
break
end
ref[key] = ref[key] or {}
if is_list(ref[key]) then
ref = ref[key][#ref[key]]
else
ref = ref[key]
end
i = i + 1
end
ref[keys[#keys]] = ref[keys[#keys]] or {}
table.insert(ref[keys[#keys]], value)
end
return M

55
test/lua/test_toml.lua Normal file
View File

@ -0,0 +1,55 @@
local toml = require('spacevim.api.data.toml')
print(vim.inspect(toml.parse_file('mode/basic.toml')))
print(vim.inspect(vim.api.nvim_eval("SpaceVim#api#import('data#toml').parse_file('mode/basic.toml')")))
-- {
-- layers = { {
-- auto_completion_return_key_behavior = "complete",
-- auto_completion_tab_key_behavior = "cycle",
-- name = "autocomplete"
-- }, {
-- default_height = 30,
-- default_position = "top",
-- name = "shell"
-- } },
-- options = {
-- buffer_index_type = 4,
-- colorscheme = "gruvbox",
-- colorscheme_bg = "dark",
-- enable_guicolors = false,
-- enable_statusline_mode = false,
-- enable_tabline_filetype_icon = false,
-- statusline_iseparator = "bar",
-- statusline_separator = "nil",
-- statusline_unicode = false,
-- vimcompatible = true,
-- windows_index_type = 3
-- }
-- }
-- {
-- layers = { {
-- auto_completion_return_key_behavior = "complete",
-- auto_completion_tab_key_behavior = "cycle",
-- name = "autocomplete"
-- }, {
-- default_height = 30,
-- default_position = "top",
-- name = "shell"
-- } },
-- options = {
-- buffer_index_type = 4,
-- colorscheme = "gruvbox",
-- colorscheme_bg = "dark",
-- enable_guicolors = 0,
-- enable_statusline_mode = 0,
-- enable_tabline_filetype_icon = 0,
-- statusline_iseparator = "bar",
-- statusline_separator = "nil",
-- statusline_unicode = 0,
-- vimcompatible = 1,
-- windows_index_type = 3
-- }
-- }