forked from lix-project/lix
Merge pull request #5240 from edolstra/builtin-help
nix --help: Display help using lowdown instead of man
This commit is contained in:
commit
16d4922dd2
|
@ -89,7 +89,7 @@ let
|
|||
in
|
||||
|
||||
let
|
||||
manpages = processCommand { filename = "nix"; command = "nix"; def = command; };
|
||||
manpages = processCommand { filename = "nix"; command = "nix"; def = builtins.fromJSON command; };
|
||||
summary = concatStrings (map (manpage: " - [${manpage.command}](command-ref/new-cli/${manpage.name})\n") manpages);
|
||||
in
|
||||
(listToAttrs manpages) // { "SUMMARY.md" = summary; }
|
||||
|
|
|
@ -44,7 +44,7 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
|
|||
|
||||
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
|
||||
@rm -rf $@
|
||||
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix (builtins.fromJSON (builtins.readFile $<))'
|
||||
$(trace-gen) $(nix-eval) --write-to $@ --expr 'import doc/manual/generate-manpage.nix (builtins.readFile $<)'
|
||||
|
||||
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
|
||||
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1617481909,
|
||||
"narHash": "sha256-SqnfOFuLuVRRNeVJr1yeEPJue/qWoCp5N6o5Kr///p4=",
|
||||
"lastModified": 1628247802,
|
||||
"narHash": "sha256-4XSXGYvKqogR7bubyqYNwBHYCtrIn6XRGXj6+u+BXNs=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "148f9b2f586c41b7e36e73009db43ea68c7a1a4d",
|
||||
"rev": "b4483d0ef85990f54b864158ab786b4a5b3904fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"ref": "VERSION_0_8_4",
|
||||
"ref": "VERSION_0_8_6",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
description = "The purely functional package manager";
|
||||
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-21.05-small";
|
||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown/VERSION_0_8_4"; flake = false; };
|
||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown/VERSION_0_8_6"; flake = false; };
|
||||
|
||||
outputs = { self, nixpkgs, lowdown-src }:
|
||||
|
||||
|
@ -350,7 +350,7 @@
|
|||
};
|
||||
|
||||
lowdown = with final; stdenv.mkDerivation rec {
|
||||
name = "lowdown-0.8.4";
|
||||
name = "lowdown-0.8.6";
|
||||
|
||||
/*
|
||||
src = fetchurl {
|
||||
|
|
|
@ -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));
|
||||
|
||||
fileParseCache[path2] = e;
|
||||
cacheFile(path, resolvedPath, e, v, mustBeTrivial);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::resetFileCache()
|
||||
{
|
||||
fileEvalCache.clear();
|
||||
fileParseCache.clear();
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}}
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue