nix-community.nixvim/lib/utils.internal.nix
Matt Sturgeon 5cf8cb5ee6
lib/utils: split into public and internal files
We will include the public file in the docs.
2025-05-19 00:23:20 +01:00

348 lines
8 KiB
Nix

{ lib }:
rec {
# Whether a string contains something other than whitespaces
hasContent = str: builtins.match "[[:space:]]*" str == null;
# Concatenate a list of strings, adding a newline at the end of each one,
# but skipping strings containing only whitespace characters
concatNonEmptyLines = lines: lib.concatLines (builtins.filter hasContent lines);
/**
Add a prefix to the keys of an attrs.
# Example
```nix
applyPrefixToAttrs "prefix_" { foo = 1; bar = 2; }
=> { prefix_foo = 1; prefix_bar = 2; }
```
# Type
```
applyPrefixToAttrs :: String -> AttrSet -> AttrSet
```
*/
applyPrefixToAttrs = prefix: lib.mapAttrs' (n: lib.nameValuePair (prefix + n));
/*
Convert a string from camelCase to snake_case
Type: string -> string
*/
toSnakeCase =
let
splitByWords = builtins.split "([A-Z])";
processWord = s: if lib.isString s then s else "_" + lib.toLower (lib.elemAt s 0);
in
string:
let
words = splitByWords string;
in
lib.concatStrings (map processWord words);
/**
Those helpers control the lua sections split in `pre, content, post`
*/
mkBeforeSection = lib.mkOrder 300;
mkAfterSection = lib.mkOrder 2000;
/**
Capitalize a string by making the first character uppercase.
# Example
```nix
upperFirstChar "hello, world!"
=> "Hello, world!"
```
# Type
```
upperFirstChar :: String -> String
```
*/
upperFirstChar =
s:
let
first = lib.substring 0 1 s;
rest = lib.substring 1 (lib.stringLength s) s;
result = (lib.toUpper first) + rest;
in
lib.optionalString (s != "") result;
mkIfNonNull' = x: y: (lib.mkIf (x != null) y);
mkIfNonNull = x: (mkIfNonNull' x x);
ifNonNull' = x: y: if (x == null) then null else y;
/**
Convert the given string into a literalExpression mkRaw.
For use in option documentation, such as examples and defaults.
# Example
```nix
literalLua "print('hi')"
=> literalExpression ''lib.nixvim.mkRaw "print('hi')"''
=> {
_type = "literalExpression";
text = ''lib.nixvim.mkRaw "print('hi')"'';
}
```
# Type
```
literalLua :: String -> AttrSet
```
*/
literalLua =
r:
let
# Pass the value through mkRaw for validation
raw = lib.nixvim.mkRaw r;
# TODO: consider switching to lib.generators.mkLuaInline ?
exp = "lib.nixvim.mkRaw " + lib.generators.toPretty { } raw.__raw;
in
lib.literalExpression exp;
__messagePrefix = scope: "Nixvim (${scope}):";
/**
Process one or several assertions by prepending the common 'Nixvim (<scope>): ' prefix to messages.
The second argument can either be a list of assertions or a single one.
# Example
```nix
assertions = mkAssertions "plugins.foo" {
assertion = plugins.foo.settings.barIntegration && (!plugins.bar.enable);
message = "`barIntegration` is enabled but the `bar` plugin is not."
}
```
# Type
```
mkAssertions :: String -> List -> List
```
*/
mkAssertions =
scope: assertions:
let
prefix = __messagePrefix scope;
processAssertion = a: {
inherit (a) assertion;
message = "${prefix} ${lib.trim a.message}";
};
in
builtins.map processAssertion (lib.toList assertions);
/**
Convert one or several conditional warnings to a final warning list.
The second argument can either be a list of _conditional warnings_ or a single one.
# Example
```nix
warnings = mkWarnings "plugins.foo" {
when = plugins.foo.settings.barIntegration && (!plugins.bar.enable);
message = "`barIntegration` is enabled but the `bar` plugin is not."
}
```
# Type
```
mkWarnings :: String -> List -> List
```
*/
mkWarnings =
scope: warnings:
let
prefix = __messagePrefix scope;
processWarning =
warning: lib.optional (warning.when or true) "${prefix} ${lib.trim (warning.message or warning)}";
in
builtins.concatMap processWarning (lib.toList warnings);
/**
Convert the given string into a `__pretty` printed mkRaw expression.
For use in nested values that will be printed by `lib.generators.toPretty`,
or `lib.options.renderOptionValue`.
# Example
```nix
nestedLiteralLua "print('hi')"
=> nestedLiteral (literalLua "print('hi')")
=> {
__pretty = lib.getAttr "text";
val = literalLua "print('hi')";
}
```
# Type
```
nestedLiteralLua :: String -> AttrSet
```
*/
nestedLiteralLua = r: nestedLiteral (literalLua r);
/**
Convert the given string or literalExpression into a `__pretty` printed expression.
For use in nested values that will be printed by `lib.generators.toPretty`,
or `lib.options.renderOptionValue`.
# Examples
```nix
nestedLiteral "example"
=> {
__pretty = lib.getAttr "text";
val = literalExpression "example";
}
```
```nix
nestedLiteral (literalExpression ''"hello, world"'')
=> {
__pretty = lib.getAttr "text";
val = literalExpression ''"hello, world"'';
}
```
# Type
```
nestedLiteral :: (String | literalExpression) -> AttrSet
```
*/
nestedLiteral = val: {
__pretty = lib.getAttr "text";
val = if val._type or null == "literalExpression" then val else lib.literalExpression val;
};
wrapDo = string: ''
do
${string}
end
'';
/**
Convert the given String to a Lua [long literal].
For example, you could use this to safely pass a Vimscript string to the
`vim.cmd` function.
[long literal]: https://www.lua.org/manual/5.4/manual.html#3.1
# Examples
```nix
nix-repl> toLuaLongLiteral "simple"
"[[simple]]"
```
```nix
nix-repl> toLuaLongLiteral "]]"
"[=[]]]=]"
```
# Type
```
toLuaLongLiteral :: String -> String
```
*/
toLuaLongLiteral =
string:
let
findTokens =
depth:
let
infix = lib.strings.replicate depth "=";
tokens.open = "[${infix}[";
tokens.close = "]${infix}]";
in
if lib.hasInfix tokens.close string then findTokens (depth + 1) else tokens;
tokens = findTokens 0;
in
tokens.open + string + tokens.close;
/**
Convert the given String into a Vimscript [:let-heredoc].
For example, you could use this to invoke [:lua].
[:let-heredoc]: https://neovim.io/doc/user/eval.html#%3Alet-heredoc
[:lua]: https://neovim.io/doc/user/lua.html#%3Alua-heredoc
# Examples
```nix
toVimscriptHeredoc "simple"
=> "<< EOF\nsimple\nEOF"
```
```nix
toVimscriptHeredoc "EOF"
=> "<< EOFF\nEOF\nEOFF"
```
# Type
```
toVimscriptHeredoc :: String -> String
```
*/
toVimscriptHeredoc =
string:
let
findToken =
depth:
let
token = "EOF" + lib.strings.replicate depth "F";
in
if lib.hasInfix token string then findToken (depth + 1) else token;
token = findToken 0;
in
''
<< ${token}
${string}
${token}'';
# Wrap Vimscript for using in lua,
# but only if the string contains something other than whitespaces
wrapVimscriptForLua =
string: lib.optionalString (hasContent string) "vim.cmd(${toLuaLongLiteral string})";
# Wrap lua script for using in Vimscript,
# but only if the string contains something other than whitespaces
wrapLuaForVimscript =
string: lib.optionalString (hasContent string) "lua ${toVimscriptHeredoc string}";
# Split a list into a several sub-list, each with a max-size of `size`
groupListBySize =
size: list:
lib.reverseList (
lib.foldl' (
lists: item:
let
first = lib.head lists;
rest = lib.drop 1 lists;
in
if lists == [ ] then
[ [ item ] ]
else if lib.length first < size then
[ (first ++ [ item ]) ] ++ rest
else
[ [ item ] ] ++ lists
) [ ] list
);
}