aboutsummaryrefslogtreecommitdiff
path: root/src/nix/repl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix/repl.cc')
-rw-r--r--src/nix/repl.cc163
1 files changed, 87 insertions, 76 deletions
diff --git a/src/nix/repl.cc b/src/nix/repl.cc
index 8409c7574..bb067e935 100644
--- a/src/nix/repl.cc
+++ b/src/nix/repl.cc
@@ -32,13 +32,19 @@ extern "C" {
#include "globals.hh"
#include "command.hh"
#include "finally.hh"
+#include "markdown.hh"
+#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
#include <gc/gc_cpp.h>
+#endif
namespace nix {
-struct NixRepl : gc
+struct NixRepl
+ #if HAVE_BOEHMGC
+ : gc
+ #endif
{
string curDir;
ref<EvalState> state;
@@ -59,7 +65,7 @@ struct NixRepl : gc
void mainLoop(const std::vector<std::string> & files);
StringSet completePrefix(string prefix);
bool getLine(string & input, const std::string &prompt);
- Path getDerivationPath(Value & v);
+ StorePath getDerivationPath(Value & v);
bool processLine(string line);
void loadFile(const Path & path);
void initEnv();
@@ -208,7 +214,7 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)
try {
if (!removeWhitespace(input).empty() && !processLine(input)) return;
} catch (ParseError & e) {
- if (e.msg().find("unexpected $end") != std::string::npos) {
+ if (e.msg().find("unexpected end of file") != std::string::npos) {
// For parse errors on incomplete input, we continue waiting for the next line of
// input without clearing the input so far.
continue;
@@ -216,9 +222,9 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)
printMsg(lvlError, e.msg());
}
} catch (Error & e) {
- printMsg(lvlError, e.msg());
+ printMsg(lvlError, e.msg());
} catch (Interrupted & e) {
- printMsg(lvlError, e.msg());
+ printMsg(lvlError, e.msg());
}
// We handled the current input fully, so we should clear it
@@ -339,24 +345,6 @@ StringSet NixRepl::completePrefix(string prefix)
}
-static int runProgram(const string & program, const Strings & args)
-{
- Strings args2(args);
- args2.push_front(program);
-
- Pid pid;
- pid = fork();
- if (pid == -1) throw SysError("forking");
- if (pid == 0) {
- restoreAffinity();
- execvp(program.c_str(), stringsToCharPtrs(args2).data());
- _exit(1);
- }
-
- return pid.wait();
-}
-
-
bool isVarName(const string & s)
{
if (s.size() == 0) return false;
@@ -372,13 +360,16 @@ bool isVarName(const string & s)
}
-Path NixRepl::getDerivationPath(Value & v) {
+StorePath NixRepl::getDerivationPath(Value & v) {
auto drvInfo = getDerivation(*state, v, false);
if (!drvInfo)
throw Error("expression does not evaluate to a derivation, so I can't build it");
- Path drvPath = drvInfo->queryDrvPath();
- if (drvPath == "" || !state->store->isValidPath(state->store->parseStorePath(drvPath)))
- throw Error("expression did not evaluate to a valid derivation");
+ Path drvPathRaw = drvInfo->queryDrvPath();
+ if (drvPathRaw == "")
+ throw Error("expression did not evaluate to a valid derivation (no drv path)");
+ StorePath drvPath = state->store->parseStorePath(drvPathRaw);
+ if (!state->store->isValidPath(drvPath))
+ throw Error("expression did not evaluate to a valid derivation (invalid drv path)");
return drvPath;
}
@@ -398,6 +389,7 @@ bool NixRepl::processLine(string line)
}
if (command == ":?" || command == ":help") {
+ // FIXME: convert to Markdown, include in the 'nix repl' manpage.
std::cout
<< "The following commands are available:\n"
<< "\n"
@@ -413,7 +405,8 @@ bool NixRepl::processLine(string line)
<< " :r Reload all files\n"
<< " :s <expr> Build dependencies of derivation, then start nix-shell\n"
<< " :t <expr> Describe result of evaluation\n"
- << " :u <expr> Build derivation, then start nix-shell\n";
+ << " :u <expr> Build derivation, then start nix-shell\n"
+ << " :doc <expr> Show documentation of a builtin function\n";
}
else if (command == ":a" || command == ":add") {
@@ -438,11 +431,11 @@ bool NixRepl::processLine(string line)
Pos pos;
- if (v.type == tPath || v.type == tString) {
+ if (v.type() == nPath || v.type() == nString) {
PathSet context;
auto filename = state->coerceToString(noPos, v, context);
pos.file = state->symbols.create(filename);
- } else if (v.type == tLambda) {
+ } else if (v.isLambda()) {
pos = v.lambda.fun->pos;
} else {
// assume it's a derivation
@@ -453,7 +446,7 @@ bool NixRepl::processLine(string line)
auto args = editorFor(pos);
auto editor = args.front();
args.pop_front();
- runProgram(editor, args);
+ runProgram(editor, true, args);
// Reload right after exiting the editor
state->resetFileCache();
@@ -471,29 +464,32 @@ bool NixRepl::processLine(string line)
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
state->callFunction(f, v, result, Pos());
- Path drvPath = getDerivationPath(result);
- runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath});
+ StorePath drvPath = getDerivationPath(result);
+ runProgram(settings.nixBinDir + "/nix-shell", true, {state->store->printStorePath(drvPath)});
}
else if (command == ":b" || command == ":i" || command == ":s") {
Value v;
evalString(arg, v);
- Path drvPath = getDerivationPath(v);
+ StorePath drvPath = getDerivationPath(v);
+ Path drvPathRaw = state->store->printStorePath(drvPath);
if (command == ":b") {
/* We could do the build in this process using buildPaths(),
but doing it in a child makes it easier to recover from
problems / SIGINT. */
- if (runProgram(settings.nixBinDir + "/nix", Strings{"build", "--no-link", drvPath}) == 0) {
- auto drv = readDerivation(*state->store, drvPath, Derivation::nameFromPath(state->store->parseStorePath(drvPath)));
+ try {
+ runProgram(settings.nixBinDir + "/nix", true, {"build", "--no-link", drvPathRaw});
+ auto drv = state->store->readDerivation(drvPath);
std::cout << std::endl << "this derivation produced the following outputs:" << std::endl;
- for (auto & i : drv.outputs)
- std::cout << fmt(" %s -> %s\n", i.first, state->store->printStorePath(i.second.path(*state->store, drv.name)));
+ for (auto & i : drv.outputsAndOptPaths(*state->store))
+ std::cout << fmt(" %s -> %s\n", i.first, state->store->printStorePath(*i.second.second));
+ } catch (ExecError &) {
}
} else if (command == ":i") {
- runProgram(settings.nixBinDir + "/nix-env", Strings{"-i", drvPath});
+ runProgram(settings.nixBinDir + "/nix-env", true, {"-i", drvPathRaw});
} else {
- runProgram(settings.nixBinDir + "/nix-shell", Strings{drvPath});
+ runProgram(settings.nixBinDir + "/nix-shell", true, {drvPathRaw});
}
}
@@ -506,6 +502,29 @@ bool NixRepl::processLine(string line)
else if (command == ":q" || command == ":quit")
return false;
+ else if (command == ":doc") {
+ Value v;
+ evalString(arg, v);
+ if (auto doc = state->getDoc(v)) {
+ std::string markdown;
+
+ if (!doc->args.empty() && doc->name) {
+ auto args = doc->args;
+ for (auto & arg : args)
+ arg = "*" + arg + "*";
+
+ markdown +=
+ "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` "
+ + concatStringsSep(" ", args) + "\n\n";
+ }
+
+ markdown += trim(stripIndentation(doc->doc));
+
+ std::cout << renderMarkdownToTerminal(markdown);
+ } else
+ throw Error("value does not have documentation");
+ }
+
else if (command != "")
throw Error("unknown command '%1%'", command);
@@ -518,10 +537,8 @@ bool NixRepl::processLine(string line)
isVarName(name = removeWhitespace(string(line, 0, p))))
{
Expr * e = parseString(string(line, p + 1));
- auto v = state->allocValue();
- v->type = tThunk;
- v->thunk.env = env;
- v->thunk.expr = e;
+ Value & v(*state->allocValue());
+ v.mkThunk(env, e);
addVarToScope(state->symbols.create(name), v);
} else {
Value v;
@@ -637,31 +654,31 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
state->forceValue(v);
- switch (v.type) {
+ switch (v.type()) {
- case tInt:
+ case nInt:
str << ANSI_CYAN << v.integer << ANSI_NORMAL;
break;
- case tBool:
+ case nBool:
str << ANSI_CYAN << (v.boolean ? "true" : "false") << ANSI_NORMAL;
break;
- case tString:
+ case nString:
str << ANSI_YELLOW;
printStringValue(str, v.string.s);
str << ANSI_NORMAL;
break;
- case tPath:
+ case nPath:
str << ANSI_GREEN << v.path << ANSI_NORMAL; // !!! escaping?
break;
- case tNull:
+ case nNull:
str << ANSI_CYAN "null" ANSI_NORMAL;
break;
- case tAttrs: {
+ case nAttrs: {
seen.insert(&v);
bool isDrv = state->isDerivation(v);
@@ -706,9 +723,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
break;
}
- case tList1:
- case tList2:
- case tListN:
+ case nList:
seen.insert(&v);
str << "[ ";
@@ -729,22 +744,21 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
str << "]";
break;
- case tLambda: {
- std::ostringstream s;
- s << v.lambda.fun->pos;
- str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
- break;
- }
-
- case tPrimOp:
- str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
- break;
-
- case tPrimOpApp:
- str << ANSI_BLUE "«primop-app»" ANSI_NORMAL;
+ case nFunction:
+ if (v.isLambda()) {
+ std::ostringstream s;
+ s << v.lambda.fun->pos;
+ str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
+ } else if (v.isPrimOp()) {
+ str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
+ } else if (v.isPrimOpApp()) {
+ str << ANSI_BLUE "«primop-app»" ANSI_NORMAL;
+ } else {
+ abort();
+ }
break;
- case tFloat:
+ case nFloat:
str << v.fpoint;
break;
@@ -794,14 +808,11 @@ struct CmdRepl : StoreCommand, MixEvalArgs
return "start an interactive environment for evaluating Nix expressions";
}
- Examples examples() override
+ std::string doc() override
{
- return {
- {
- "Display all special commands within the REPL:",
- "nix repl\n nix-repl> :?"
- }
- };
+ return
+ #include "repl.md"
+ ;
}
void run(ref<Store> store) override
@@ -816,6 +827,6 @@ struct CmdRepl : StoreCommand, MixEvalArgs
}
};
-static auto r1 = registerCommand<CmdRepl>("repl");
+static auto rCmdRepl = registerCommand<CmdRepl>("repl");
}