diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2021-09-13 14:41:28 +0200 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2021-09-13 14:45:21 +0200 |
commit | 49a932fb18add471feefc469fb45fc44313bd5c6 (patch) | |
tree | 716a8abc8682f190b22d1b2fecb4ed45bb87ae55 /src | |
parent | 14205debb244b925d685e3645c4deef937fa78ec (diff) |
nix --help: Display help using lowdown instead of man
Fixes #4476.
Fixes #5231.
Diffstat (limited to 'src')
-rw-r--r-- | src/libexpr/eval.cc | 43 | ||||
-rw-r--r-- | src/libexpr/eval.hh | 8 | ||||
-rw-r--r-- | src/libutil/args.cc | 1 | ||||
-rw-r--r-- | src/libutil/args.hh | 8 | ||||
-rw-r--r-- | src/nix/local.mk | 4 | ||||
-rw-r--r-- | src/nix/main.cc | 46 |
6 files changed, 87 insertions, 23 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 76541da8b..bc41a2cd9 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -895,23 +895,41 @@ void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial) return; } - Path path2 = resolveExprPath(path); - if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) { + Path resolvedPath = resolveExprPath(path); + if ((i = fileEvalCache.find(resolvedPath)) != fileEvalCache.end()) { v = i->second; return; } - printTalkative("evaluating file '%1%'", path2); + printTalkative("evaluating file '%1%'", resolvedPath); Expr * e = nullptr; - auto j = fileParseCache.find(path2); + auto j = fileParseCache.find(resolvedPath); if (j != fileParseCache.end()) e = j->second; if (!e) - e = parseExprFromFile(checkSourcePath(path2)); + e = parseExprFromFile(checkSourcePath(resolvedPath)); + + cacheFile(path, resolvedPath, e, v, mustBeTrivial); +} + + +void EvalState::resetFileCache() +{ + fileEvalCache.clear(); + fileParseCache.clear(); +} - fileParseCache[path2] = e; + +void EvalState::cacheFile( + const Path & path, + const Path & resolvedPath, + Expr * e, + Value & v, + bool mustBeTrivial) +{ + fileParseCache[resolvedPath] = e; try { // Enforce that 'flake.nix' is a direct attrset, not a @@ -921,19 +939,12 @@ void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial) throw EvalError("file '%s' must be an attribute set", path); eval(e, v); } catch (Error & e) { - addErrorTrace(e, "while evaluating the file '%1%':", path2); + addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath); throw; } - fileEvalCache[path2] = v; - if (path != path2) fileEvalCache[path] = v; -} - - -void EvalState::resetFileCache() -{ - fileEvalCache.clear(); - fileParseCache.clear(); + fileEvalCache[resolvedPath] = v; + if (path != resolvedPath) fileEvalCache[path] = v; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 93e1ef05f..b29feb134 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -172,6 +172,14 @@ public: trivial (i.e. doesn't require arbitrary computation). */ void evalFile(const Path & path, Value & v, bool mustBeTrivial = false); + /* Like `cacheFile`, but with an already parsed expression. */ + void cacheFile( + const Path & path, + const Path & resolvedPath, + Expr * e, + Value & v, + bool mustBeTrivial = false); + void resetFileCache(); /* Look up a file in the search path. */ diff --git a/src/libutil/args.cc b/src/libutil/args.cc index afed0670f..9df279faf 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -331,6 +331,7 @@ MultiCommand::MultiCommand(const Commands & commands_) if (i == commands.end()) throw UsageError("'%s' is not a recognised command", s); command = {s, i->second()}; + command->second->parent = this; }} }); diff --git a/src/libutil/args.hh b/src/libutil/args.hh index c08ba8abd..22c94b501 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -12,6 +12,8 @@ namespace nix { enum HashType : char; +class MultiCommand; + class Args { public: @@ -169,11 +171,13 @@ public: virtual nlohmann::json toJSON(); friend class MultiCommand; + + MultiCommand * parent = nullptr; }; /* A command is an argument parser that can be executed by calling its run() method. */ -struct Command : virtual Args +struct Command : virtual public Args { friend class MultiCommand; @@ -193,7 +197,7 @@ typedef std::map<std::string, std::function<ref<Command>()>> Commands; /* An argument parser that supports multiple subcommands, i.e. ‘<command> <subcommand>’. */ -class MultiCommand : virtual Args +class MultiCommand : virtual public Args { public: Commands commands; diff --git a/src/nix/local.mk b/src/nix/local.mk index 83b6dd08b..e4ec7634d 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -14,7 +14,7 @@ nix_SOURCES := \ $(wildcard src/nix-instantiate/*.cc) \ $(wildcard src/nix-store/*.cc) \ -nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain -I src/libcmd +nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain -I src/libcmd -I doc/manual nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd @@ -30,3 +30,5 @@ src/nix-env/user-env.cc: src/nix-env/buildenv.nix.gen.hh src/nix/develop.cc: src/nix/get-env.sh.gen.hh src/nix-channel/nix-channel.cc: src/nix-channel/unpack-channel.nix.gen.hh + +src/nix/main.cc: doc/manual/generate-manpage.nix.gen.hh doc/manual/utils.nix.gen.hh diff --git a/src/nix/main.cc b/src/nix/main.cc index 008482be3..8aaf08813 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -10,6 +10,7 @@ #include "filetransfer.hh" #include "finally.hh" #include "loggers.hh" +#include "markdown.hh" #include <sys/types.h> #include <sys/socket.h> @@ -163,9 +164,43 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs } }; -static void showHelp(std::vector<std::string> subcommand) +/* Render the help for the specified subcommand to stdout using + lowdown. */ +static void showHelp(std::vector<std::string> subcommand, MultiCommand & toplevel) { - showManPage(subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand))); + auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); + + evalSettings.restrictEval = false; + evalSettings.pureEval = false; + EvalState state({}, openStore("dummy://")); + + auto vGenerateManpage = state.allocValue(); + state.eval(state.parseExprFromString( + #include "generate-manpage.nix.gen.hh" + , "/"), *vGenerateManpage); + + auto vUtils = state.allocValue(); + state.cacheFile( + "/utils.nix", "/utils.nix", + state.parseExprFromString( + #include "utils.nix.gen.hh" + , "/"), + *vUtils); + + auto vJson = state.allocValue(); + mkString(*vJson, toplevel.toJSON().dump()); + + auto vRes = state.allocValue(); + state.callFunction(*vGenerateManpage, *vJson, *vRes, noPos); + + auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); + if (!attr) + throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); + + auto markdown = state.forceString(*attr->value); + + RunPager pager; + std::cout << renderMarkdownToTerminal(markdown) << "\n"; } struct CmdHelp : Command @@ -194,7 +229,10 @@ struct CmdHelp : Command void run() override { - showHelp(subcommand); + assert(parent); + MultiCommand * toplevel = parent; + while (toplevel->parent) toplevel = toplevel->parent; + showHelp(subcommand, *toplevel); } }; @@ -277,7 +315,7 @@ void mainWrapped(int argc, char * * argv) } else break; } - showHelp(subcommand); + showHelp(subcommand, args); return; } catch (UsageError &) { if (!completions) throw; |