lib: fix escaping bugs in wrapVimscriptForLua and wrapLuaForVimscript

These functions had very similar bugs: they didn't check if their chosen
"close token" was already present in the string they're escaping.

I went ahead and did the work implied by the TODOs: search for a "close
token" that is *not* in the original string. Pretty simple concept, but
it turned into an annoying amount of code. I couldn't find anything in
upstream nixpkgs lib, or some clever insight about lua/vimscript that
makes this work unecessary, but I'll be thrilled (and a little bummed
about a wasted afternoon) to learn about something.
This commit is contained in:
Jeremy Fleischman 2024-09-18 14:41:21 -07:00
parent 2df1bdd14d
commit 400d1d927d
No known key found for this signature in database
GPG key ID: 19319CD8416A642B
2 changed files with 120 additions and 14 deletions

View file

@ -125,27 +125,98 @@ rec {
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
# TODO: account for a possible ']]' in the string
wrapVimscriptForLua =
string:
lib.optionalString (hasContent string) ''
vim.cmd([[
${string}
]])
'';
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
# TODO: account for a possible 'EOF' if the string
wrapLuaForVimscript =
string:
lib.optionalString (hasContent string) ''
lua << EOF
${string}
EOF
'';
string: lib.optionalString (hasContent string) "lua ${toVimscriptHeredoc string}";
# Split a list into a several sub-list, each with a max-size of `size`
groupListBySize =

View file

@ -374,6 +374,41 @@ let
];
};
};
testEscapeStringForLua = {
expr = lib.mapAttrs (_: helpers.utils.toLuaLongLiteral) {
simple = "simple";
depth-one = " ]] ";
depth-two = " ]] ]=] ";
};
expected = {
simple = "[[simple]]";
depth-one = "[=[ ]] ]=]";
depth-two = "[==[ ]] ]=] ]==]";
};
};
testEscapeStringForVimscript = {
expr = lib.mapAttrs (_: helpers.utils.toVimscriptHeredoc) {
simple = "simple";
depth-one = "EOF";
depth-two = "EOF EOFF";
};
expected = {
simple = ''
<< EOF
simple
EOF'';
depth-one = ''
<< EOFF
EOF
EOFF'';
depth-two = ''
<< EOFFF
EOF EOFF
EOFFF'';
};
};
};
in
if results == [ ] then