home/nvim: add varargs; uppercase dsl keywords; some cleanup

This commit is contained in:
chayleaf 2023-03-17 05:04:50 +07:00
parent 4c463b22a4
commit 7c1bcf409e
3 changed files with 347 additions and 277 deletions

View file

@ -18,6 +18,57 @@
identLines = lines: builtins.concatStringsSep "\n" (map (x: " ${x}") lines); identLines = lines: builtins.concatStringsSep "\n" (map (x: " ${x}") lines);
ident = code: identLines (lib.splitString "\n" code); ident = code: identLines (lib.splitString "\n" code);
# convert list into pairs
pairsv = ret: list: key: if list == [] then {
list = ret;
leftover = key;
} else pairsk (ret ++ [[key (builtins.head list)]]) (builtins.tail list);
pairsk = ret: list: if list == [] then {
list = ret;
leftover = null;
} else pairsv ret (builtins.tail list) (builtins.head list);
# list end
end = list: builtins.elemAt list (builtins.length list - 1);
# pop list end
pop = list: lib.take (builtins.length list - 1) list;
luaType = val:
if builtins.isAttrs val && val?__kind then (
if val?_type then val._type
# can't know the type of arbitrary expressions!
else null
) else if builtins.isList val || builtins.isAttrs val then "table"
else if builtins.isPath val || builtins.isString val then "string"
else if builtins.isInt val || builtins.isFloat val then "number"
else if builtins.isNull val then "nil"
else if builtins.isFunction val then let info = getInfo val; in (
if info != null && info?_expr then luaType info._expr
else if info != null && info?_stmt then luaType info._stmt
else "function"
) else if builtins.isBool val then "boolean"
else null;
# vararg system
getInfo = func: if builtins.isFunction func && builtins.functionArgs func == {} then (
let ret = builtins.tryEval (func {__GET_INFO = true;}); in if ret.success then ret.value else null
) else null;
isGetInfo = arg: arg == { __GET_INFO = true; };
argsSink = key: args: finally: arg:
if isGetInfo arg then
{${key} = finally args;}
else if builtins.isAttrs arg && arg?__kind && arg.__kind == "unroll" then
{${key} = finally (args ++ arg.list);}
else
argsSink key (args ++ [arg]) finally;
# The following functions may take state: moduleName and scope
# scope is how many variables are currently in scope
# the count is used for generating new variable names
pushScope = n: { moduleName, scope }: { inherit moduleName; scope = scope + n; };
pushScope1 = pushScope 1;
# wrap an expression in parentheses if necessary # wrap an expression in parentheses if necessary
# probably not the best heuristics, but good enough to make the output readable # probably not the best heuristics, but good enough to make the output readable
wrapSafe = s: (builtins.match "^[-\"a-zA-Z0-9_.()]*$" s) != null; wrapSafe = s: (builtins.match "^[-\"a-zA-Z0-9_.()]*$" s) != null;
@ -27,32 +78,32 @@
keySafe = s: (builtins.match "^[a-zA-Z_][_a-zA-Z0-9]*$" s) != null; keySafe = s: (builtins.match "^[a-zA-Z_][_a-zA-Z0-9]*$" s) != null;
wrapKey = scope: s: if keySafe s then s else "[${compileExpr scope s}]"; wrapKey = scope: s: if keySafe s then s else "[${compileExpr scope s}]";
# The following functions take state: sname and scope applyVars' = origScope: count: prefix: let self = (scope: func: argc:
# sname is module name let info = getInfo func; in
# scope is how many variables are currently in scope if info != null && info?_expr then self scope info._expr argc
# the count is used for generating new variable names else if info != null && info?_stmt then self scope info._stmt argc
else if count != null && scope == (origScope + count) then { result = func; }
compileFunc' = argn: sc@{sname,scope}: id: func: else if count == null && !builtins.isFunction func then { result = func; inherit argc; }
(if builtins.isFunction func else self (scope + 1) (let
then
(compileFunc'
(argn + 1)
sc
id
(func (let
args = builtins.functionArgs func; args = builtins.functionArgs func;
rawVar = var "${sname}_${id}_arg${builtins.toString (scope + argn)}"; name = "${prefix}${builtins.toString scope}"; in
in if args == {} if args == {} then func (RAW name)
then rawVar else func (builtins.mapAttrs (k: v: RAW "${name}.${k}") args)) (argc + 1)
else builtins.mapAttrs (k: v: prop rawVar k) args ); in self;
))) applyVars = count: prefix: scope: func: applyVars' scope count prefix scope func 0;
else ''
function ${id}(${builtins.concatStringsSep ", " (builtins.genList (n: "${sname}_${id}_arg${builtins.toString (scope + n)}") argn)})
${ident (compileStmt {inherit sname;scope = scope + argn;} func)}
end'');
compileFunc = compileFunc' 0;
compileExpr = sc: func: ( compileFunc = state@{moduleName, scope}: id: expr:
let
res = applyVars null "${moduleName}_${id}_arg" scope expr;
argc = res.argc;
func = res.result;
header = if id == "" then "function" else "local function ${id}";
in ''
${header}(${builtins.concatStringsSep ", " (builtins.genList (n: "${moduleName}_${id}_arg${builtins.toString (scope + n)}") argc)})
${ident (compileStmt (pushScope argc state) func)}
end'';
compileExpr = state: func: (
if builtins.isString func then if builtins.isString func then
if lib.hasInfix "\n" func then '' if lib.hasInfix "\n" func then ''
[[ [[
@ -62,181 +113,183 @@
else if builtins.isFloat func then builtins.toString func else if builtins.isFloat func then builtins.toString func
else if builtins.isBool func then (if func then "true" else "false") else if builtins.isBool func then (if func then "true" else "false")
else if builtins.isNull func then "nil" else if builtins.isNull func then "nil"
else if builtins.isPath func then compileExpr sc (builtins.toString func) else if builtins.isPath func then compileExpr state (builtins.toString func)
else if builtins.isFunction func then let else if builtins.isFunction func then let
info = if builtins.functionArgs func == {} then (func "GET_INFO") else null; in info = getInfo func; in
if builtins.isAttrs info && info?_name if info != null && info?_name then
then info._name info._name
else (compileFunc sc "" func) else if info != null && info?_expr then
compileExpr state info._expr
else if info != null && info?_stmt then
assert false; null
else (compileFunc state "" func)
else if builtins.isList func then '' else if builtins.isList func then ''
{ {
${ident (builtins.concatStringsSep "\n" (map (x: (compileExpr sc x) + ";" ) func))} ${ident (builtins.concatStringsSep "\n" (map (x: (compileExpr state x) + ";" ) func))}
}'' }''
else if builtins.isAttrs func && func?_expr then compileExpr state func._expr
else if builtins.isAttrs func && !(func?__kind) then '' else if builtins.isAttrs func && !(func?__kind) then ''
{ {
${ident (builtins.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${wrapKey sc k} = ${compileExpr sc v};") func))} ${ident (builtins.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${wrapKey state k} = ${compileExpr state v};") func))}
}'' }''
else if func.__kind == "var" then else if func.__kind == "var" then
"${func._name}" "${func._name}"
else if func.__kind == "op2" then else if func.__kind == "op2" then
builtins.concatStringsSep func.op (map (x: wrapExpr (compileExpr sc x)) func.args) builtins.concatStringsSep " ${func.op} " (map (x: wrapExpr (compileExpr state x)) func.args)
else if func.__kind == "defun" then else if func.__kind == "defun" then
(compileFunc sc (if func?id then func.id else "") func.func) (compileFunc state (if func?id then func.id else "") func.func)
else if func.__kind == "prop" then else if func.__kind == "prop" then
assert lib.assertMsg (luaType func.expr == null || luaType func.expr == "table") "Unable to get property ${func.name} of a ${luaType func.expr}!"; assert lib.assertMsg (luaType func.expr == null || luaType func.expr == "table") "Unable to get property ${func.name} of a ${luaType func.expr}!";
"${wrapExpr (compileExpr sc func.expr)}.${func.name}" "${wrapExpr (compileExpr state func.expr)}.${func.name}"
else if func.__kind == "call" then else if func.__kind == "call" then
let args = if builtins.isList func._args then func._args else [func._args]; in let args = func._args; in
assert lib.assertMsg assert lib.assertMsg
((!(func._func?_minArity) || (builtins.length args) >= func._func._minArity) && (!(func._func?_maxArity) || (builtins.length args) <= func._func._maxArity)) ((!(func._func?_minArity) || (builtins.length args) >= func._func._minArity) && (!(func._func?_maxArity) || (builtins.length args) <= func._func._maxArity))
"error: wrong function arity for ${compileExpr sc func._func}! expected at least ${builtins.toString func._func._minArity}; found ${builtins.toString (builtins.length args)}"; "error: wrong function arity for ${compileExpr state func._func}! expected at least ${builtins.toString func._func._minArity}; found ${builtins.toString (builtins.length args)}";
"${wrapExpr (compileExpr sc func._func)}(${builtins.concatStringsSep ", " (map (compileExpr sc) args)})" "${wrapExpr (compileExpr state func._func)}(${builtins.concatStringsSep ", " (map (compileExpr state) args)})"
else if func.__kind == "mcall" then else if func.__kind == "mcall" then
"${wrapExpr (compileExpr sc func.val)}:${func.name}(${builtins.concatStringsSep ", " (map (compileExpr sc) (if builtins.isList func.args then func.args else [func.args]))})" "${wrapExpr (compileExpr state func.val)}:${func.name}(${builtins.concatStringsSep ", " (map (compileExpr state) func.args)})"
else if func.__kind == "tableAttr" then else if func.__kind == "tableAttr" then
assert lib.assertMsg (luaType func.table == null || luaType func.table == "table") "Unable to get table value ${compileExpr sc func.key} of a ${luaType func.table} ${compileExpr sc func.table}!"; assert lib.assertMsg (luaType func.table == null || luaType func.table == "table") "Unable to get table value ${compileExpr state func.key} of a ${luaType func.table} ${compileExpr state func.table}!";
"${wrapExpr (compileExpr sc func.table)}[${compileExpr sc func.key}]" "${wrapExpr (compileExpr state func.table)}[${compileExpr state func.key}]"
else null else assert lib.assertMsg false "Invalid kind ${func.__kind}"; null
); );
luaType = val: compileStmt = state@{moduleName,scope}: func: (
if builtins.isAttrs val && val?__kind then ( if builtins.isList func then builtins.concatStringsSep "\n" (map (compileStmt state) func)
if val?_type then val._type else if builtins.isAttrs func && func?_stmt then compileStmt state func._stmt
else null
) else if builtins.isList val || builtins.isAttrs val then "table"
else if builtins.isPath val || builtins.isString val then "string"
else if builtins.isInt val || builtins.isFloat val then "number"
else if builtins.isNull val then "nil"
else if builtins.isFunction val then "function"
else if builtins.isBool val then "boolean"
else null;
compileStmt = sc@{sname,scope}: func: (
if builtins.isList func then builtins.concatStringsSep "\n" (map (compileStmt sc) func)
else if builtins.isAttrs func && (func?__kind) then ( else if builtins.isAttrs func && (func?__kind) then (
if func.__kind == "assign" then if func.__kind == "assign" then
assert lib.assertMsg assert lib.assertMsg
(luaType func.expr == null || luaType func.val == null || luaType func.val == func.expr._type) (luaType func.expr == null || luaType func.val == null || luaType func.val == func.expr._type)
"error: setting ${compileExpr sc func.expr} to wrong type. It should be ${luaType func.expr} but is ${luaType func.val}"; "error: setting ${compileExpr state func.expr} to wrong type. It should be ${luaType func.expr} but is ${luaType func.val}";
"${compileExpr sc func.expr} = ${compileExpr sc func.val}" "${compileExpr state func.expr} = ${compileExpr state func.val}"
else if func.__kind == "bind" then else if func.__kind == "bind" then
"local ${func.name} = ${compileExpr sc func.val}" "local ${func.name} = ${compileExpr state func.val}"
else if func.__kind == "let" then '' else if func.__kind == "let" then ''
${builtins.concatStringsSep "\n" (lib.imap0 (n: val: ${builtins.concatStringsSep "\n" (lib.imap0 (n: val:
"local ${sname}_var${builtins.toString (scope + n)} = ${ "local ${moduleName}_var${builtins.toString (scope + n)} = ${
compileExpr sc val compileExpr state val
}") func.vals)} }") func.vals)}
${let vals = func.vals; origScope = scope; apply = { scope, func }: if scope == (origScope + (builtins.length vals)) then func else apply { ${
scope = scope + 1; let res = applyVars (builtins.length func.vals) "${moduleName}_var" scope func.func; in
func = func (raw "${sname}_var${builtins.toString scope}"); compileStmt (pushScope (builtins.length func.vals) state) res.result
}; in
compileStmt {inherit sname;scope = scope + (builtins.length func.vals);} (apply { inherit scope; inherit (func) func; })
}'' }''
else if func.__kind == "letrec" then '' else if func.__kind == "letrec" then let argc = builtins.length func.vals; in ''
${builtins.concatStringsSep "\n" (lib.imap0 (n: val: ${builtins.concatStringsSep "\n" (lib.imap0 (n: val:
"local ${sname}_var${builtins.toString (scope + n)} = ${ "local ${moduleName}_var${builtins.toString (scope + n)} = ${
let vals = func.vals; origScope = scope; apply = { scope, func }: if scope == (origScope + (builtins.length vals)) then func else apply { let res = applyVars argc "${moduleName}_var" scope val; in
scope = scope + 1; compileExpr (pushScope argc state) res.result
func = func (raw "${sname}_var${builtins.toString scope}");
}; in
compileExpr {inherit sname;scope = scope + (builtins.length func.vals);} (apply { inherit scope; func = val; })
}") func.vals)} }") func.vals)}
${let vals = func.vals; origScope = scope; apply = { scope, func }: if scope == (origScope + (builtins.length vals)) then func else apply { ${
scope = scope + 1; let res = applyVars argc "${moduleName}_var" scope func.func; in
func = func (raw "${sname}_var${builtins.toString scope}"); compileStmt (pushScope (builtins.length func.vals) state) res.result
}; in
compileStmt {inherit sname;scope = scope + (builtins.length func.vals);} (apply { inherit scope; inherit (func) func; })
}'' }''
else if func.__kind == "for" then let else if func.__kind == "for" then let
varNames = builtins.genList (n: "${sname}_var${builtins.toString (scope + n)}") func.n; res = applyVars null "${moduleName}_var" scope func.body;
scope' = { inherit sname; scope = scope + 1; }; varNames = builtins.genList (n: "${moduleName}_var${builtins.toString (scope + n)}") res.argc;
in '' in ''
for ${builtins.concatStringsSep "," varNames} in ${compileExpr scope' func.expr} do for ${builtins.concatStringsSep "," varNames} in ${compileExpr scope func.expr} do
${ ${
let argn = func.n; origScope = scope; apply = { scope, func }: if scope == (origScope + argn) then func else apply { ident (compileStmt (pushScope1 state) res.result)
scope = scope + 1;
func = func (raw "${sname}_var${builtins.toString scope}");
}; in
ident (compileStmt scope' (apply { inherit scope; func = func.body; }))
} }
end'' end''
else if func.__kind == "return" then else if func.__kind == "return" then
"return ${compileExpr sc func.expr}" "return ${compileExpr state func.expr}"
else if func.__kind == "if" then else if func.__kind == "if" then
(lib.removeSuffix "else" ((builtins.concatStringsSep "" (map (lib.removeSuffix "else" ((builtins.concatStringsSep "" (map
(cond: '' (cond: ''
if ${compileExpr sc (builtins.elemAt cond 0)} then if ${compileExpr state (builtins.elemAt cond 0)} then
${ident (compileStmt sc (builtins.elemAt cond 1))} ${ident (compileStmt state (builtins.elemAt cond 1))}
else'') else'')
func.conds)) func.conds))
+ (if func.fallback != null then "\n${ident (compileStmt sc func.fallback)}\n" else ""))) + "end" + (if func.fallback != null then "\n${ident (compileStmt state func.fallback)}\n" else ""))) + "end"
else compileExpr sc func else compileExpr state func
) else compileExpr sc func ) else if builtins.isFunction func then (let
info = getInfo func; in
if info != null && info?_stmt then compileStmt state info._stmt
else compileExpr state func
) else compileExpr state func
); );
# compile a module # compile a module
compile = sname: input: (compileStmt {inherit sname;scope=1;} input) + "\n"; compile = moduleName: input: (compileStmt { inherit moduleName; scope = 1; } input) + "\n";
# pass some raw code to lua directly # pass some raw code to lua directly
var = name: { __kind = "var"; _name = name; }; VAR = name: { __kind = "var"; _name = name; };
raw = var; RAW = VAR;
# Access a property # Access a property
# Corresponding lua code: table.property # Corresponding lua code: table.property
# expr -> identifier -> expr # expr -> identifier -> expr
prop = expr: name: { __kind = "prop"; inherit expr name; }; PROP = expr: name: { __kind = "prop"; inherit expr name; };
# Escape a list so it can be passed to vararg functions
UNROLL = list: { __kind = "unroll"; inherit list; };
# Apply a list of arguments to a function/operator (probably more useful than the above)
APPLY = func: list: func (UNROLL list);
# Call a function # Call a function
# Useful if you need to call a zero argument function, or if you need to handle some weird metatable stuff
# corresponding lua code: someFunc() # corresponding lua code: someFunc()
# expr -> [args] -> expr | expr -> arg1 -> expr # expr -> arg1 -> ... -> argN -> expr
call = func: args: { __kind = "call"; _func = func; _args = args; }; CALL = func: argsSink "_expr" [] (args: { __kind = "call"; _func = func; _args = args; });
# Call a method # Call a method
# corresponding lua code: someTable:someFunc() # corresponding lua code: someTable:someFunc()
# expr -> identifier -> [args] -> expr | expr -> identifier -> arg1 -> expr # expr -> identifier -> arg1 -> ... -> argN -> expr
mcall = val: name: args: { __kind = "mcall"; inherit val name args; }; MCALL = val: name: argsSink "_expr" [] (args: { __kind = "mcall"; inherit val name args; });
# corresponding lua code: = # corresponding lua code: =
# expr -> expr -> stmt # expr -> expr -> stmt
set = expr: val: { __kind = "assign"; inherit expr val; }; SET = expr: val: { __kind = "assign"; inherit expr val; };
# opName -> [exprs] -> expr | opName -> expr1 -> ... -> exprN -> expr
OP2 = op: argsSink "_expr" [] (args: { __kind = "op2"; inherit op args; });
# opName -> expr1 -> expr2 -> expr | opName -> [exprs] -> expr
op2 = op: args:
if builtins.isList args then { __kind = "op2"; inherit op args; }
else (secondArg: { __kind = "op2"; inherit op; args = [ args secondArg ]; })
;
# The following all have the signature # The following all have the signature
# expr1 -> expr2 -> expr2 | [exprs] -> expr # expr1 -> ... -> exprN -> expr
eq = op2 "=="; EQ = OP2 "==";
# gt = op2 ">"; # GT = OP2 ">";
# ge = op2 ">="; # GE = OP2 ">=";
# ne = op2 "~="; # NE = OP2 "~=";
# and = op2 "and"; # AND = OP2 "and";
# or = op2 "or"; OR = OP2 "or";
# Corresponding lua code: for # Corresponding lua code: for ... in ...
# argc -> expr -> (expr1 -> ... -> exprN -> stmts) -> stmts # argc -> expr -> (expr1 -> ... -> exprN -> stmts) -> stmts
# forin = n: expr: body: { __kind = "for"; inherit n expr body; }; # FORIN = expr: body: { __kind = "for"; inherit expr body; };
# Issues a return statement # Issues a return statement
# Corresponding lua code: return # Corresponding lua code: return
# expr -> stmt # expr -> stmt
return = expr: { __kind = "return"; inherit expr; }; RETURN = expr: { __kind = "return"; inherit expr; };
# Creates a zero argument function with user-provided statements # Creates a zero argument function with user-provided statements
# stmts -> expr # stmts -> expr
defun = func: { __kind = "defun"; inherit func; }; DEFUN = func: { __kind = "defun"; inherit func; };
# Corresponding lua code: if then else # Corresponding lua code: if then (else?)
# [[cond expr]] -> fallbackExpr -> stmts # [[cond expr]] -> fallbackExpr? -> stmts
ifelse = conds: fallback: { __kind = "if"; inherit fallback; conds = if builtins.isList (builtins.elemAt conds 0) then conds else [conds]; }; IFELSE' = conds: fallback: { __kind = "if"; inherit fallback; conds = if builtins.isList (builtins.elemAt conds 0) then conds else [conds]; };
# Corresponding lua code: if then # Corresponding lua code: if then (else?)
# [[cond expr]] -> > stmts # (expr -> stmts ->)* (fallback expr ->)? stmts
# ifnoelse = conds: ifelse conds null; IF = argsSink "_stmt" [] (args:
let pairs = pairsk [] args; in
if pairs.leftover == null && builtins.length pairs.list > 1 && builtins.elemAt (end pairs.list) 0 == ELSE
then IFELSE' (pop pairs.list) (builtins.elemAt (end pairs.list) 1)
else IFELSE' pairs.list pairs.leftover
);
# Signifies the fallback branch in IF. May only be the last branch.
# Note that you may also omit it and just include the last branch without a preceding condition.
ELSE = true;
# Corresponding lua code: table[key] # Corresponding lua code: table[key]
# table -> key -> expr # table -> key -> expr
tableAttr = table: key: { __kind = "tableAttr"; inherit table key; }; ATTR = table: key: { __kind = "tableAttr"; inherit table key; };
# Directly creates a local varible with your chosen name # Directly creates a local varible with your chosen name
# But why would you use this??? # But why would you use this???
@ -244,23 +297,29 @@
# Creates variables and passes them to the function # Creates variables and passes them to the function
# Corresponding lua code: local ... = ... # Corresponding lua code: local ... = ...
# [expr] -> (expr1 -> ... -> exprN -> stmt) -> stmt # expr1 -> (expr -> stmt) -> stmt | [expr] -> (expr1 -> ... -> exprN -> stmt) -> stmt
bind = vals: func: if builtins.isList vals then { __kind = "let"; inherit vals func; } else bind [ vals ] func; LET = vals: func: if builtins.isList vals then { __kind = "let"; inherit vals func; } else LET [ vals ] func;
# Creates variables and passes them to the function as well as variable binding code # Creates variables and passes them to the function as well as variable binding code
# Corresponding lua code: local ... = ... # Corresponding lua code: local ... = ...
# [(expr1 -> ... -> exprN -> expr)] -> (expr1 -> ... -> exprN -> stmt) -> stmt # (expr1 -> expr) -> (expr1 -> stmt) -> stmt | [(expr1 -> ... -> exprN -> expr)] -> (expr1 -> ... -> exprN -> stmt) -> stmt
bindrec = vals: func: if builtins.isList vals then { __kind = "letrec"; inherit vals func; } else bindrec [ vals ] func; LETREC = vals: func: if builtins.isList vals then { __kind = "letrec"; inherit vals func; } else LETREC [ vals ] func;
# "type definitions" for neovim # "type definitions" for neovim
defs = pkgs.callPackage ./vim-opts.nix { inherit raw call; plugins = config.programs.neovim.plugins; }; defs = pkgs.callPackage ./vim-opts.nix { inherit RAW CALL isGetInfo compileExpr; inherit (config.programs.neovim) plugins; };
reqbind = name: func: bind [ (defs.require name) ] (result: func (defs._reqbind name result._name));
reqbindGen = names: func:
if names == [] then func
else result: reqbindGen (builtins.tail names) (func (defs._reqbind (builtins.head names) result._name));
# bind a value to a require
REQBIND = name: func:
if builtins.isList name
then LET (map defs.require name) (reqbindGen name func)
else LET [ (defs.require name) ] (result: func (defs._reqbind name result._name));
in with defs; let in with defs; let
# require = name: call (var "require") [ name ]; vimcmd = name: CALL (RAW "vim.cmd.${name}");
# setup = plugin: opts: call (prop plugin "setup") [ opts ]; vimg = name: PROP vim.g name;
# vimfn = name: call (raw "vim.fn.${name}");
vimcmd = name: call (raw "vim.cmd.${name}");
vimg = name: prop vim.g name;
keymapSetSingle = opts@{ keymapSetSingle = opts@{
mode, mode,
lhs, lhs,
@ -274,7 +333,7 @@
k != "keys" && k != "mode" && k != "lhs" && k != "rhs" && k != "desc" k != "keys" && k != "mode" && k != "lhs" && k != "rhs" && k != "desc"
# defaults to false # defaults to false
&& ((k != "silent" && k != "noremap") || (builtins.isBool v && v))) opts''; && ((k != "silent" && k != "noremap") || (builtins.isBool v && v))) opts'';
in vim.keymap.set [ mode lhs rhs opts' ]; in vim.keymap.set mode lhs rhs opts';
keymapSetMulti = opts@{ keymapSetMulti = opts@{
keys, keys,
mode, mode,
@ -290,16 +349,16 @@
in (lib.mapAttrsToList (k: {rhs, desc}: keymapSetSingle (opts' // { in (lib.mapAttrsToList (k: {rhs, desc}: keymapSetSingle (opts' // {
lhs = k; inherit rhs; lhs = k; inherit rhs;
})) keys) ++ [ })) keys) ++ [
(which-key.register [(lib.mapAttrs (k: v: [v.rhs v.desc]) keys) opts']) (which-key.register (lib.mapAttrs (k: v: [v.rhs v.desc]) keys) opts')
]; ];
keymapSetNs = args: keymapSetMulti (args // { mode = "n"; }); keymapSetNs = args: keymapSetMulti (args // { mode = "n"; });
kmSetNs = keys: keymapSetNs { inherit keys; }; kmSetNs = keys: keymapSetNs { inherit keys; };
keymapSetVs = args: keymapSetMulti (args // { mode = "v"; }); keymapSetVs = args: keymapSetMulti (args // { mode = "v"; });
kmSetVs = keys: keymapSetVs { inherit keys; }; kmSetVs = keys: keymapSetVs { inherit keys; };
which-key = req "which-key"; which-key = REQ "which-key";
luasnip = req "luasnip"; luasnip = REQ "luasnip";
cmp = req "cmp"; cmp = REQ "cmp";
in { in {
enable = true; enable = true;
defaultEditor = true; defaultEditor = true;
@ -330,42 +389,51 @@
vimdiffAlias = true; vimdiffAlias = true;
extraLuaConfig = (compile "main" [ extraLuaConfig = (compile "main" [
(set (vimg "vimsyn_embed") "l") (SET (vimg "vimsyn_embed") "l")
(bind (vim.api.nvim_create_augroup [ "nvimrc" { clear = true; } ]) (group: (LET (vim.api.nvim_create_augroup "nvimrc" { clear = true; }) (group:
map (au: let au' = lib.filterAttrs (k: v: k != "event") au; lib.mapAttrsToList (k: v: vim.api.nvim_create_autocmd k { inherit group; callback = v; }) {
in vim.api.nvim_create_autocmd [ au.event ({ BufReadPre = DEFUN (SET vim.o.foldmethod "syntax");
inherit group; BufEnter = { buf, ... }:
} // au') ] (LET (vim.filetype.match { inherit buf; }) (filetype: [
) [ (IF (APPLY OR (map (EQ filetype) [ "gitcommit" "markdown" ])) (LET vim.o.colorcolumn (old_colorcolumn: [
{ event = "FileType"; (SET vim.o.colorcolumn "73")
pattern = ["markdown" "gitcommit"]; (vim.api.nvim_create_autocmd "BufLeave" {
# must be a string callback = DEFUN [
callback = defun (set vim.o.colorcolumn "73"); } (SET vim.o.colorcolumn old_colorcolumn)
{ event = "FileType"; # return true = delete autocommand
pattern = ["markdown"]; (RETURN true)
# must be a number... ];
callback = defun (set vim.o.textwidth 72); } })
{ event = "BufReadPre"; ])))
callback = defun (set vim.o.foldmethod "syntax"); } (IF (EQ filetype "markdown") (LET vim.o.textwidth (old_textwidth: [
{ event = "BufWinEnter"; (SET vim.o.textwidth 72)
callback = { buf, ... }: (vim.api.nvim_create_autocmd "BufLeave" {
(bind (vim.filetype.match { inherit buf; }) (filetype: [ callback = DEFUN [
(vimcmd "folddoc" [ "foldopen!" ]) (SET vim.o.textwidth old_textwidth)
(ifelse [(eq filetype "gitcommit") [ (RETURN true)
(call vim.cmd { ];
})
])))
]));
BufWinEnter = { buf, ... }:
(LET (vim.filetype.match { inherit buf; }) (filetype: [
(vimcmd "folddoc" "foldopen!")
(IF (EQ filetype "gitcommit")
(CALL vim.cmd {
cmd = "normal"; cmd = "normal";
bang = true; bang = true;
args = [ "gg" ]; args = [ "gg" ];
}) })
]] ELSE
(call vim.cmd { (CALL vim.cmd {
cmd = "normal"; cmd = "normal";
bang = true; bang = true;
args = [ "g`\"" ]; args = [ "g`\"" ];
}) })
) )
])); } ]));
])) # END }
))
]); ]);
plugins = let ps = pkgs.vimPlugins; in map (x: if x?config && x?plugin then { type = "lua"; } // x else x) [ plugins = let ps = pkgs.vimPlugins; in map (x: if x?config && x?plugin then { type = "lua"; } // x else x) [
ps.vim-svelte ps.vim-svelte
@ -382,7 +450,7 @@
}; };
}; };
config = compile "vscode_nvim" [ config = compile "vscode_nvim" [
((req "vscode").setup { ((REQ "vscode").setup {
transparent = true; transparent = true;
color_overrides = { color_overrides = {
vscGray = "#745b5f"; vscGray = "#745b5f";
@ -400,25 +468,25 @@
vscPink = "#cf83c4"; vscPink = "#cf83c4";
}; };
}) })
(vim.api.nvim_set_hl [ 0 "NormalFloat" { (vim.api.nvim_set_hl 0 "NormalFloat" {
bg = "NONE"; bg = "NONE";
}]) })
]; } ]; }
{ plugin = ps.nvim-web-devicons; { plugin = ps.nvim-web-devicons;
config = compile "nvim_web_devicons" ((req "nvim-web-devicons").setup {}); } config = compile "nvim_web_devicons" ((REQ "nvim-web-devicons").setup {}); }
{ plugin = ps.nvim-tree-lua; { plugin = ps.nvim-tree-lua;
config = compile "nvim_tree_lua" [ config = compile "nvim_tree_lua" (REQBIND ["nvim-tree" "nvim-tree.api"] (nvim-tree: nvim-tree-api: [
(set (vimg "loaded_netrw") 1) (SET (vimg "loaded_netrw") 1)
(set (vimg "loaded_netrwPlugin") 1) (SET (vimg "loaded_netrwPlugin") 1)
(set vim.o.termguicolors true) (SET vim.o.termguicolors true)
((req "nvim-tree").setup {}) # :help nvim-tree-setup (nvim-tree.setup {}) # :help nvim-tree-setup
(kmSetNs { (kmSetNs {
"<C-N>" = { "<C-N>" = {
rhs = (req "nvim-tree.api").tree.toggle; rhs = nvim-tree-api.tree.toggle;
desc = "Toggle NvimTree"; desc = "Toggle NvimTree";
}; };
}) })
]; } ])); }
ps.vim-sleuth ps.vim-sleuth
ps.luasnip ps.luasnip
{ plugin = ps.nvim-cmp; { plugin = ps.nvim-cmp;
@ -433,11 +501,11 @@
[ "" name ] [ "" name ]
[ "" name ] [ "" name ]
]); ]);
in compile "nvim_cmp" (reqbind "cmp" (cmp: in compile "nvim_cmp" (REQBIND [ "cmp" "lspkind" ] (cmp: lspkind:
# call is required because cmp.setup is a table # call is required because cmp.setup is a table
(call cmp.setup { (CALL cmp.setup {
snippet = { snippet = {
expand = { body, ... }: luasnip.lsp_expand [ body {} ]; expand = { body, ... }: luasnip.lsp_expand body {};
}; };
view = { }; view = { };
window = { window = {
@ -450,80 +518,67 @@
}; };
}; };
formatting = { formatting = {
format = _: vim_item: let kind = prop vim_item "kind"; in [ format = _: vim_item: let kind = PROP vim_item "kind"; in [
(set (SET kind (string.format "%s %s" (ATTR lspkind kind) kind))
kind (RETURN vim_item)
(string.format [
"%s %s"
(tableAttr (req "lspkind") kind)
kind
]))
(return vim_item)
]; ];
}; };
mapping = { mapping = {
"<C-p>" = cmp.mapping.select_prev_item [{}]; "<C-p>" = cmp.mapping.select_prev_item {};
"<C-n>" = cmp.mapping.select_next_item [{}]; "<C-n>" = cmp.mapping.select_next_item {};
"<C-space>" = cmp.mapping.complete [{}]; "<C-space>" = cmp.mapping.complete {};
"<C-e>" = cmp.mapping.close [{}]; "<C-e>" = cmp.mapping.close {};
"<cr>" = cmp.mapping.confirm { "<cr>" = cmp.mapping.confirm {
behavior = cmp.ConfirmBehavior.Replace; behavior = cmp.ConfirmBehavior.Replace;
select = false; select = false;
}; };
"<tab>" = call cmp.mapping [(fallback: "<tab>" = CALL cmp.mapping (fallback:
(ifelse [[(cmp.visible []) (IF
(cmp.select_next_item [])] (CALL cmp.visible)
/*elseif*/ [(luasnip.expand_or_jumpable []) (CALL cmp.select_next_item)
(vim.api.nvim_feedkeys [ (CALL luasnip.expand_or_jumpable)
(vim.api.nvim_replace_termcodes [ "<Plug>luasnip-expand-or-jump" true true true ]) (vim.api.nvim_feedkeys
(vim.api.nvim_replace_termcodes "<Plug>luasnip-expand-or-jump" true true true)
"" ""
false false)
]) ELSE
]] # else (CALL fallback)))
(call fallback []) [ "i" "s" ];
)) "<S-tab>" = CALL cmp.mapping (fallback:
[ "i" "s" ] (IF
]; (CALL cmp.visible)
"<S-tab>" = call cmp.mapping [(fallback: (CALL cmp.select_prev_item)
(ifelse [[(cmp.visible []) (luasnip.jumpable (-1))
(cmp.select_prev_item [])] (vim.api.nvim_feedkeys
/*elseif*/ [(luasnip.jumpable [ (-1) ]) (vim.api.nvim_replace_termcodes "<Plug>luasnip-jump-prev" true true true)
(vim.api.nvim_feedkeys [
(vim.api.nvim_replace_termcodes [ "<Plug>luasnip-jump-prev" true true true ])
"" ""
false false)
]) ELSE
]] # else (CALL fallback)))
(call fallback []) [ "i" "s" ];
))
[ "i" "s" ]
];
}; };
sources = cmp.config.sources [[ sources = cmp.config.sources [
{ name = "nvim_lsp"; } { name = "nvim_lsp"; }
{ name = "luasnip"; } { name = "luasnip"; }
]]; ];
}) })
)); } )); }
ps.lspkind-nvim ps.lspkind-nvim
ps.cmp_luasnip ps.cmp_luasnip
ps.cmp-nvim-lsp ps.cmp-nvim-lsp
{ plugin = ps.nvim-autopairs; { plugin = ps.nvim-autopairs;
config = compile "nvim_autopairs" (reqbind "nvim-autopairs.completion.cmp" (cmp_autopairs: [ config = compile "nvim_autopairs" (REQBIND ["nvim-autopairs.completion.cmp" "nvim-autopairs"] (cmp-autopairs: nvim-autopairs: [
((req "nvim-autopairs").setup { (nvim-autopairs.setup {
disable_filetype = [ "TelescopePrompt" "vim" ]; disable_filetype = [ "TelescopePrompt" "vim" ];
}) })
(mcall cmp.event "on" [ (MCALL cmp.event "on" "confirm_done" (cmp-autopairs.on_confirm_done {}))
"confirm_done"
(cmp_autopairs.on_confirm_done [{}])
])
])); } ])); }
{ plugin = ps.comment-nvim; { plugin = ps.comment-nvim;
config = compile "comment_nvim" [ config = compile "comment_nvim" [
((req "Comment").setup {}) ((REQ "Comment").setup {})
(kmSetNs { (kmSetNs {
"<space>/" = { "<space>/" = {
rhs = prop (req "Comment.api").toggle "linewise.current"; rhs = PROP (REQ "Comment.api").toggle "linewise.current";
desc = "Comment current line"; desc = "Comment current line";
}; };
}) })
@ -535,7 +590,13 @@
}) })
]; } ]; }
{ plugin = ps.nvim-lspconfig; { plugin = ps.nvim-lspconfig;
config = compile "nvim_lspconfig" (let setupLsp = lsp: builtins.seq (req "lspconfig.server_configurations.${lsp}") (call (prop (req "lspconfig") "${lsp}.setup")); in [ config = compile "nvim_lspconfig" (
let lsp = name: builtins.seq
# ensure an lsp exists (otherwise lspconfig will still create an empty config for some reason)
(REQ "lspconfig.server_configurations.${name}")
# metatables, son! they harden in response to physical trauma
(REQ' (PROP (require "lspconfig") name));
in [
# See `:help vim.diagnostic.*` for documentation on any of the below functions # See `:help vim.diagnostic.*` for documentation on any of the below functions
(kmSetNs { (kmSetNs {
"<space>e" = { "<space>e" = {
@ -555,11 +616,11 @@
desc = "Add buffer diagnostics to the location list."; desc = "Add buffer diagnostics to the location list.";
}; };
}) })
(bind [ (LET [
# LET on_attach # LET on_attach
(client: bufnr: ([ (client: bufnr: [
# Enable completion triggered by <c-x><c-o> # Enable completion triggered by <c-x><c-o>
(vim.api.nvim_buf_set_option [ bufnr "omnifunc" "v:lua.vim.lsp.omnifunc" ]) (vim.api.nvim_buf_set_option bufnr "omnifunc" "v:lua.vim.lsp.omnifunc")
# Mappings. # Mappings.
# See `:help vim.lsp.*` for documentation on any of the below functions # See `:help vim.lsp.*` for documentation on any of the below functions
(kmSetNs { (kmSetNs {
@ -585,9 +646,7 @@
rhs = vim.lsp.buf.remove_workspace_folder; rhs = vim.lsp.buf.remove_workspace_folder;
desc = "Remove a folder from the workspace folders."; }; desc = "Remove a folder from the workspace folders."; };
"<space>wl" = { "<space>wl" = {
rhs = (defun (print [ rhs = DEFUN (print (CALL vim.inspect (CALL vim.lsp.buf.list_workspace_folders)));
(call vim.inspect [(vim.lsp.buf.list_workspace_folders [])])
]));
desc = "List workspace folders."; }; desc = "List workspace folders."; };
"<space>D" = { "<space>D" = {
rhs = vim.lsp.buf.type_definition; rhs = vim.lsp.buf.type_definition;
@ -602,10 +661,10 @@
rhs = vim.lsp.buf.references; rhs = vim.lsp.buf.references;
desc = "Lists all the references to the symbol under the cursor in the quickfix window."; }; desc = "Lists all the references to the symbol under the cursor in the quickfix window."; };
"<space>f" = { "<space>f" = {
rhs = (defun (vim.lsp.buf.format {async = true;})); rhs = (DEFUN (vim.lsp.buf.format {async = true;}));
desc = "Formats a buffer."; }; desc = "Formats a buffer."; };
}) })
])) ])
# LET rust_settings # LET rust_settings
{ rust-analyzer = { { rust-analyzer = {
assist.emitMustUse = true; assist.emitMustUse = true;
@ -614,36 +673,36 @@
procMacro.enable = true; procMacro.enable = true;
}; } }; }
# LET capabilities # LET capabilities
(vim.tbl_extend [ (vim.tbl_extend
"keep" "keep"
((req "cmp_nvim_lsp").default_capabilities [{}]) ((REQ "cmp_nvim_lsp").default_capabilities {})
(vim.lsp.protocol.make_client_capabilities []) (CALL vim.lsp.protocol.make_client_capabilities))
])
# BEGIN # BEGIN
] (on_attach: rust_settings: capabilities: [ ] (on_attach: rust_settings: capabilities: [
(bindrec (LETREC
# LETREC on_attach_rust # LETREC on_attach_rust
(on_attach_rust: client: bufnr: [ (on_attach_rust: client: bufnr: [
(vim.api.nvim_create_user_command ["RustAndroid" (opts: [ (vim.api.nvim_create_user_command "RustAndroid" (opts: [
(vim.lsp.set_log_level "debug") (vim.lsp.set_log_level "debug")
(setupLsp "rust_analyzer" { ((lsp "rust_analyzer").setup {
on_attach = on_attach_rust; on_attach = on_attach_rust;
inherit capabilities; inherit capabilities;
settings = vim.tbl_deep_extend [ settings = vim.tbl_deep_extend
"keep" "keep"
config.rustAnalyzerAndroidSettings config.rustAnalyzerAndroidSettings
rust_settings rust_settings;
];
}) })
]) {}]) ]) {})
(call on_attach [client bufnr]) (CALL on_attach client bufnr)
]) ])
# BEGIN # BEGIN
(let lsp' = { name, settings ? {} }: setupLsp name { (let setupLsp' = { name, settings ? {} }: (lsp name).setup {
inherit on_attach capabilities settings; inherit on_attach capabilities settings;
}; lsp = args: lsp' (if builtins.isString args then { name = args; } else args); in (on_attach_rust: [ };
setupLsp = args: setupLsp' (if builtins.isString args then { name = args; } else args);
in (on_attach_rust: [
# (vim.lsp.set_log_level "debug") # (vim.lsp.set_log_level "debug")
(map lsp [ (map setupLsp [
# see https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md # see https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
"bashls" "bashls"
"clangd" "clangd"
@ -660,7 +719,7 @@
"taplo" "taplo"
"marksman" "marksman"
]) ])
(setupLsp "rust_analyzer" { ((lsp "rust_analyzer").setup {
on_attach = on_attach_rust; on_attach = on_attach_rust;
settings = rust_settings; settings = rust_settings;
inherit capabilities; inherit capabilities;
@ -670,8 +729,8 @@
]); } ]); }
{ plugin = ps.which-key-nvim; { plugin = ps.which-key-nvim;
config = compile "which_key_nvim" [ config = compile "which_key_nvim" [
(set vim.o.timeout true) (SET vim.o.timeout true)
(set vim.o.timeoutlen 500) (SET vim.o.timeoutlen 500)
(which-key.setup {}) (which-key.setup {})
]; } ]; }
]; ];

View file

@ -58,10 +58,10 @@ local json = require "cjson"
-- mark globals before requiring package -- mark globals before requiring package
mark(_G) mark(_G)
local package = "@package@" local package = @package@
result = { __kind = "var", _type = "table", _name = "" } result = { __kind = "var", _type = "table", _name = "" }
dump2(require(package), "", result) dump2(package, "", result)
print(json.encode(result)) print(json.encode(result))

View file

@ -4,19 +4,26 @@
, neovimUtils , neovimUtils
, lua51Packages , lua51Packages
, wrapNeovimUnstable , wrapNeovimUnstable
, call , CALL
, isGetInfo
, substituteAll , substituteAll
, plugins , plugins
, compileExpr
# , extraLuaPackages ? [] # , extraLuaPackages ? []
, ... }: , ... }:
let update = self: prefix: lib.mapAttrs (k: v: let # TODO: bfs instead of dfs in var dumps
let
update = self: prefix: lib.mapAttrs (k: v: let
v' = update self prefix v; v' = update self prefix v;
in (if builtins.isAttrs v && v?__kind then ( in (if builtins.isAttrs v && v?__kind then (
if v.__kind == "rec" then if v.__kind == "rec" then
lib.attrByPath (lib.splitString "." v.path) self lib.attrByPath (lib.splitString "." v.path) null self
else if v.__kind == "var" && v._type == "function" then else if v.__kind == "var" && v._type == "function" then
(args: if args == "GET_INFO" then v' else call v' args) (args:
if isGetInfo args then v'
else CALL v' args)
else v' else v'
) else if builtins.isAttrs v then v' ) else if builtins.isAttrs v then v'
else if prefix != "" && k == "_name" then else if prefix != "" && k == "_name" then
@ -32,7 +39,7 @@ config = neovimUtils.makeNeovimConfig {
neovim = wrapNeovimUnstable neovim-unwrapped config; neovim = wrapNeovimUnstable neovim-unwrapped config;
getReqAttrs = name: builtins.fromJSON (builtins.readFile (stdenvNoCC.mkDerivation { getReqAttrs = name: builtins.fromJSON (builtins.readFile (stdenvNoCC.mkDerivation {
phases = [ "installPhase" ]; phases = [ "installPhase" ];
name = "neovim-require-${name}.json"; name = "neovim-types-${name}.json";
dumpPlugin = substituteAll { dumpPlugin = substituteAll {
src = ./dump_plugin.lua; src = ./dump_plugin.lua;
package = name; package = name;
@ -43,8 +50,12 @@ getReqAttrs = name: builtins.fromJSON (builtins.readFile (stdenvNoCC.mkDerivatio
nvim --headless -S $dumpPlugin -i NONE -u NONE -n -c 'echo""|qall!' 2>$out nvim --headless -S $dumpPlugin -i NONE -u NONE -n -c 'echo""|qall!' 2>$out
''; '';
})); }));
req = name: let result = update result "require(\"${name}\")" (getReqAttrs name); in result; req = name: let res = update res name (getReqAttrs name); in res;
_reqbind = name: varname: let result = update result "${varname}" (getReqAttrs name); in result; REQ = name: req "require(\"${name}\")";
# the code must not use external state! this can't be checked
# this could (?) be fixed with REQBIND', but I don't need it
REQ' = code: req (compileExpr { moduleName = "req"; scope = 1; } code);
_reqbind = name: varname: let s = "require(\"${name}\")"; res = update res "${varname}" (getReqAttrs s); in res;
in result // { in result // {
inherit req _reqbind; inherit REQ REQ' _reqbind;
} }