mirror of
https://github.com/nix-community/nixvim.git
synced 2025-07-13 18:54:30 +02:00
lib/lua: refactor toLuaObject, now configurable
Heavily based on nixpkgs lib.generators.toPretty
This commit is contained in:
parent
cd479ec0ef
commit
aff12581d8
3 changed files with 194 additions and 62 deletions
|
@ -26,7 +26,7 @@ rec {
|
||||||
};
|
};
|
||||||
vim-plugin = import ./vim-plugin.nix { inherit lib nixvimOptions nixvimUtils; };
|
vim-plugin = import ./vim-plugin.nix { inherit lib nixvimOptions nixvimUtils; };
|
||||||
inherit nixvimTypes;
|
inherit nixvimTypes;
|
||||||
inherit (lua) toLuaObject;
|
toLuaObject = lua.toLua;
|
||||||
}
|
}
|
||||||
// nixvimUtils
|
// nixvimUtils
|
||||||
// nixvimOptions
|
// nixvimOptions
|
||||||
|
|
230
lib/to-lua.nix
230
lib/to-lua.nix
|
@ -32,51 +32,193 @@ rec {
|
||||||
# and contain only letters, digits, and underscores.
|
# and contain only letters, digits, and underscores.
|
||||||
isIdentifier = s: !(isKeyword s) && (match "[A-Za-z_][0-9A-Za-z_]*" s) == [ ];
|
isIdentifier = s: !(isKeyword s) && (match "[A-Za-z_][0-9A-Za-z_]*" s) == [ ];
|
||||||
|
|
||||||
|
# toLua' with default options, aliased as toLuaObject at the top-level
|
||||||
|
toLua = toLua' { };
|
||||||
|
|
||||||
# Black functional magic that converts a bunch of different Nix types to their
|
# Black functional magic that converts a bunch of different Nix types to their
|
||||||
# lua equivalents!
|
# lua equivalents!
|
||||||
toLuaObject =
|
toLua' =
|
||||||
args:
|
{
|
||||||
if builtins.isAttrs args then
|
/**
|
||||||
if hasAttr "__raw" args then
|
If this option is true, attrsets like { __pretty = fn; val = …; }
|
||||||
args.__raw
|
will use fn to convert val to a lua representation.
|
||||||
else if hasAttr "__empty" args then
|
*/
|
||||||
"{ }"
|
allowPrettyValues ? false,
|
||||||
else
|
|
||||||
"{"
|
/**
|
||||||
+ (concatStringsSep ", " (
|
If this option is true, values like { __raw = "print('hi)"; }
|
||||||
mapAttrsToList (
|
will render as print('hi')
|
||||||
|
*/
|
||||||
|
allowRawValues ? true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
If this option is true, attrsets like { "__rawKey__print('hi')" = "a"; }
|
||||||
|
will render as { [print('hi')] = "a"] }
|
||||||
|
*/
|
||||||
|
allowRawAttrKeys ? true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
If this option is true, attrsets like { __unkeyed.1 = "a"; b = "b"; }
|
||||||
|
will render as { "a", b = "b" }
|
||||||
|
*/
|
||||||
|
allowUnkeyedAttrs ? true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
If this option is true, attrsets like { a = "a"; b = "b"; }
|
||||||
|
will render as { a, b = "b" } instead of { ["a"] = "a", ["b"] = "b" }
|
||||||
|
*/
|
||||||
|
allowUnquotedAttrKeys ? true,
|
||||||
|
|
||||||
|
# TODO: smartQuoteStyle: use ' or " based on string content
|
||||||
|
# TODO: allowRawStrings: use [=[ ]=] strings for multiline strings
|
||||||
|
|
||||||
|
/**
|
||||||
|
If this option is true, attrsets like { a = null; b = ""; }
|
||||||
|
will render as { ["b"] = "" }
|
||||||
|
*/
|
||||||
|
removeNullAttrValues ? true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
If this option is true, attrsets like { a = { }; b = [ ]; c = ""; }
|
||||||
|
will render as { ["c"] = "" }
|
||||||
|
*/
|
||||||
|
removeEmptyAttrValues ? true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
If this option is true, attrsets like { a.__empty = null; }
|
||||||
|
will render as { ["a"] = { } }, ignoring removeEmptyAttrValues and removeNullAttrValues.
|
||||||
|
*/
|
||||||
|
allowExplicitEmpty ? true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
If this option is true, attrsets like { __emptyString = "foo"; }
|
||||||
|
will render as { [""] = "foo" }.
|
||||||
|
|
||||||
|
This is deprecated: use an attrset like { "" = "foo"; } instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
allowLegacyEmptyStringAttr ? true,
|
||||||
|
/**
|
||||||
|
If this option is true, the output is indented with newlines for attribute sets and lists
|
||||||
|
*/
|
||||||
|
# TODO: make this true by default
|
||||||
|
multiline ? false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initial indentation level
|
||||||
|
*/
|
||||||
|
indent ? "",
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# If any of these are options are set, we need to preprocess the value
|
||||||
|
needsPreprocessing = removeNullAttrValues || removeEmptyAttrValues || allowExplicitEmpty;
|
||||||
|
|
||||||
|
# Slight optimization: only preprocess if we actually need to
|
||||||
|
preprocessValue = value: if needsPreprocessing then removeEmptiesRecursive value else value;
|
||||||
|
|
||||||
|
# Recursively filters `value`, removing any empty/null attrs (as configured)
|
||||||
|
# Does not recurse into "special" attrs, such as `__raw`
|
||||||
|
removeEmptiesRecursive =
|
||||||
|
value:
|
||||||
|
if allowPrettyValues && value ? __pretty && value ? val then
|
||||||
|
value
|
||||||
|
else if allowRawValues && value ? __raw then
|
||||||
|
value
|
||||||
|
else if isList value then
|
||||||
|
map removeEmptiesRecursive value
|
||||||
|
else if isAttrs value then
|
||||||
|
concatMapAttrs (
|
||||||
n: v:
|
n: v:
|
||||||
let
|
if removeNullAttrValues && v == null then
|
||||||
keyString =
|
{ }
|
||||||
if n == "__emptyString" then
|
else if removeEmptyAttrValues && (v == [ ] || v == { }) then
|
||||||
"['']"
|
{ }
|
||||||
else if hasPrefix "__rawKey__" n then
|
else if allowExplicitEmpty && v ? __empty then
|
||||||
"[${removePrefix "__rawKey__" n}]"
|
{ ${n} = { }; }
|
||||||
else if isIdentifier n then
|
else if isAttrs v then
|
||||||
n
|
let
|
||||||
else
|
v' = removeEmptiesRecursive v;
|
||||||
"[${toLuaObject n}]";
|
in
|
||||||
valueString = toLuaObject v;
|
if v' == { } then { } else { ${n} = v'; }
|
||||||
in
|
else
|
||||||
if hasPrefix "__unkeyed" n then valueString else "${keyString} = ${valueString}"
|
{ ${n} = v; }
|
||||||
) (filterAttrs (n: v: v != null && (toLuaObject v != "{}")) args)
|
) value
|
||||||
))
|
else
|
||||||
+ "}"
|
value;
|
||||||
else if builtins.isList args then
|
|
||||||
"{" + concatMapStringsSep ", " toLuaObject args + "}"
|
# Return the dict-style table key, formatted as per the config
|
||||||
else if builtins.isString args then
|
toTableKey =
|
||||||
# This should be enough!
|
s:
|
||||||
builtins.toJSON args
|
if allowRawAttrKeys && hasPrefix "__rawKey__" s then
|
||||||
else if builtins.isPath args then
|
"[${removePrefix "__rawKey__" s}]"
|
||||||
builtins.toJSON (toString args)
|
else if allowUnquotedAttrKeys && isIdentifier s then
|
||||||
else if builtins.isBool args then
|
s
|
||||||
"${boolToString args}"
|
else if allowLegacyEmptyStringAttr && s == "__emptyString" then
|
||||||
else if builtins.isFloat args then
|
trace ''nixvim(toLua): __emptyString is deprecated, just use an attribute named "".'' (
|
||||||
"${toString args}"
|
toTableKey ""
|
||||||
else if builtins.isInt args then
|
)
|
||||||
"${toString args}"
|
else
|
||||||
else if (args == null) then
|
"[${go "" s}]";
|
||||||
"nil"
|
|
||||||
else
|
# The main recursive function implementing `toLua`:
|
||||||
"";
|
# Visit a value and print it as lua, with the specified indentation.
|
||||||
|
# Recursively visits child values with increasing indentation.
|
||||||
|
go =
|
||||||
|
indent: v:
|
||||||
|
let
|
||||||
|
# Calculate the start/end padding, including any linebreaks,
|
||||||
|
# based on multiline config and current indentation.
|
||||||
|
introSpace = if multiline then "\n${indent} " else " ";
|
||||||
|
outroSpace = if multiline then "\n${indent}" else " ";
|
||||||
|
in
|
||||||
|
if v == null then
|
||||||
|
"nil"
|
||||||
|
else if isInt v then
|
||||||
|
toString v
|
||||||
|
# toString loses precision on floats, so we use toJSON instead.
|
||||||
|
# It can output an exponent form supported by lua.
|
||||||
|
else if isFloat v then
|
||||||
|
builtins.toJSON v
|
||||||
|
else if isBool v then
|
||||||
|
boolToString v
|
||||||
|
else if isPath v then
|
||||||
|
go indent (toString v)
|
||||||
|
else if isString v then
|
||||||
|
# TODO: support lua's escape sequences, literal string, and content-appropriate quote style
|
||||||
|
# See https://www.lua.org/pil/2.4.html
|
||||||
|
# and https://www.lua.org/manual/5.1/manual.html#2.1
|
||||||
|
# and https://github.com/NixOS/nixpkgs/blob/00ba4c2c35f5e450f28e13e931994c730df05563/lib/generators.nix#L351-L365
|
||||||
|
builtins.toJSON v
|
||||||
|
else if v == [ ] || v == { } then
|
||||||
|
"{ }"
|
||||||
|
else if isFunction v then
|
||||||
|
abort "nixvim(toLua): Unexpected function: " + generators.toPretty { } v
|
||||||
|
else if isDerivation v then
|
||||||
|
abort "nixvim(toLua): Unexpected derivation: " + generators.toPretty { } v
|
||||||
|
else if isList v then
|
||||||
|
"{" + introSpace + concatMapStringsSep ("," + introSpace) (go (indent + " ")) v + outroSpace + "}"
|
||||||
|
else if isAttrs v then
|
||||||
|
# apply pretty values if allowed
|
||||||
|
if allowPrettyValues && v ? __pretty && v ? val then
|
||||||
|
v.__pretty v.val
|
||||||
|
# apply raw values if allowed
|
||||||
|
else if allowRawValues && v ? __raw then
|
||||||
|
# TODO: apply indentation to multiline raw values
|
||||||
|
v.__raw
|
||||||
|
else
|
||||||
|
"{"
|
||||||
|
+ introSpace
|
||||||
|
+ concatStringsSep ("," + introSpace) (
|
||||||
|
mapAttrsToList (
|
||||||
|
name: value:
|
||||||
|
(if allowExplicitEmpty && hasPrefix "__unkeyed" name then "" else toTableKey name + " = ")
|
||||||
|
+ addErrorContext "while evaluating an attribute `${name}`" (go (indent + " ") value)
|
||||||
|
) v
|
||||||
|
)
|
||||||
|
+ outroSpace
|
||||||
|
+ "}"
|
||||||
|
else
|
||||||
|
abort "nixvim(toLua): should never happen (v = ${v})";
|
||||||
|
in
|
||||||
|
value: go indent (preprocessValue value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ let
|
||||||
3
|
3
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
expected = ''{foo = "bar", qux = {1, 2, 3}}'';
|
expected = ''{ foo = "bar", qux = { 1, 2, 3 } }'';
|
||||||
};
|
};
|
||||||
|
|
||||||
testToLuaObjectRawLua = {
|
testToLuaObjectRawLua = {
|
||||||
|
@ -68,7 +68,7 @@ let
|
||||||
"__unkeyed...." = "foo";
|
"__unkeyed...." = "foo";
|
||||||
bar = "baz";
|
bar = "baz";
|
||||||
};
|
};
|
||||||
expected = ''{"foo", bar = "baz"}'';
|
expected = ''{ "foo", bar = "baz" }'';
|
||||||
};
|
};
|
||||||
|
|
||||||
testToLuaObjectNestedAttrs = {
|
testToLuaObjectNestedAttrs = {
|
||||||
|
@ -81,7 +81,7 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
expected = ''{a = {b = 1, c = 2, d = {e = 3}}}'';
|
expected = ''{ a = { b = 1, c = 2, d = { e = 3 } } }'';
|
||||||
};
|
};
|
||||||
|
|
||||||
testToLuaObjectNestedList = {
|
testToLuaObjectNestedList = {
|
||||||
|
@ -98,7 +98,7 @@ let
|
||||||
]
|
]
|
||||||
7
|
7
|
||||||
];
|
];
|
||||||
expected = "{1, 2, {3, 4, {5, 6}}, 7}";
|
expected = "{ 1, 2, { 3, 4, { 5, 6 } }, 7 }";
|
||||||
};
|
};
|
||||||
|
|
||||||
testToLuaObjectNonStringPrims = {
|
testToLuaObjectNonStringPrims = {
|
||||||
|
@ -109,7 +109,7 @@ let
|
||||||
d = false;
|
d = false;
|
||||||
e = null;
|
e = null;
|
||||||
};
|
};
|
||||||
expected = ''{a = 1.000000, b = 2, c = true, d = false}'';
|
expected = ''{ a = 1.0, b = 2, c = true, d = false }'';
|
||||||
};
|
};
|
||||||
|
|
||||||
testToLuaObjectNilPrim = {
|
testToLuaObjectNilPrim = {
|
||||||
|
@ -134,7 +134,7 @@ let
|
||||||
f = { };
|
f = { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
expected = ''{}'';
|
expected = ''{ }'';
|
||||||
};
|
};
|
||||||
|
|
||||||
testToLuaObjectEmptyTable = {
|
testToLuaObjectEmptyTable = {
|
||||||
|
@ -150,17 +150,7 @@ let
|
||||||
g = helpers.emptyTable;
|
g = helpers.emptyTable;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
expected = ''{c = { }, d = {g = { }}}'';
|
expected = ''{ c = { }, d = { g = { } } }'';
|
||||||
};
|
|
||||||
|
|
||||||
testToLuaObjectQuotedKeys = {
|
|
||||||
expr = helpers.toLuaObject {
|
|
||||||
"1_a" = "a";
|
|
||||||
_b = "b";
|
|
||||||
c = "c";
|
|
||||||
d-d = "d";
|
|
||||||
};
|
|
||||||
expected = ''{["1_a"] = "a", _b = "b", c = "c", ["d-d"] = "d"}'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
testIsLuaKeyword = {
|
testIsLuaKeyword = {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue