about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--default.nix8
-rw-r--r--nix-repl.cc137
3 files changed, 105 insertions, 44 deletions
diff --git a/README.md b/README.md
index acdcd367fc9e..ac5ad98cf53c 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ Here is a typical `nix-repl` session:
     nix-repl> map (x: x * x) xs
     [ 1 4 9 16 25 ]
 
-    nix-repl> :l <nixos>
+    nix-repl> :l <nixpkgs/nixos>
     Added 7 variables.
 
     nix-repl> config.services.dhcpd
@@ -86,7 +86,7 @@ Here is a typical `nix-repl` session:
 Tab completion works on variables in scope and on attribute sets.  For
 example:
 
-    $ nix-repl  '<nixpkgs>' '<nixos>'
+    $ nix-repl  '<nixpkgs>' '<nixpkgs/nixos>'
     Welcome to Nix version 1.6pre3215_2c1ecf8. Type :? for help.
 
     nix-repl> thunder<TAB> => thunderbird
diff --git a/default.nix b/default.nix
index 8690325e5289..919082981e1e 100644
--- a/default.nix
+++ b/default.nix
@@ -4,13 +4,13 @@ with import nixpkgs { inherit system; };
 
 let nix = nixUnstable; in
 
-runCommand "nix-repl"
-  { buildInputs = [ readline nix boehmgc ]; }
+runCommandCC "nix-repl"
+  { buildInputs = [ pkgconfig readline nix boehmgc ]; }
   ''
     mkdir -p $out/bin
-    g++ -O3 -Wall -std=c++0x \
+    g++ -O3 -Wall -std=c++14 \
       -o $out/bin/nix-repl ${./nix-repl.cc} \
-      -I${nix}/include/nix \
+      $(pkg-config --cflags nix-main) \
       -lnixformat -lnixutil -lnixstore -lnixexpr -lnixmain -lreadline -lgc \
       -DNIX_VERSION=\"${(builtins.parseDrvName nix.name).version}\"
   ''
diff --git a/nix-repl.cc b/nix-repl.cc
index 1c878cd0e230..71790eb481a7 100644
--- a/nix-repl.cc
+++ b/nix-repl.cc
@@ -1,3 +1,5 @@
+#include <nix/config.h>
+
 #include <iostream>
 #include <cstdlib>
 
@@ -14,15 +16,22 @@
 #include "get-drvs.hh"
 #include "derivations.hh"
 #include "affinity.hh"
+#include "globals.hh"
 
 using namespace std;
 using namespace nix;
 
+#define ESC_RED "\033[31m"
+#define ESC_GRE "\033[32m"
+#define ESC_YEL "\033[33m"
+#define ESC_BLU "\033[34;1m"
+#define ESC_MAG "\033[35m"
+#define ESC_CYA "\033[36m"
+#define ESC_END "\033[0m"
 
 string programId = "nix-repl";
 const string historyFile = string(getenv("HOME")) + "/.nix-repl-history";
 
-
 struct NixRepl
 {
     string curDir;
@@ -39,10 +48,11 @@ struct NixRepl
     StringSet completions;
     StringSet::iterator curCompletion;
 
-    NixRepl(const Strings & searchPath);
+    NixRepl(const Strings & searchPath, nix::ref<Store> store);
     void mainLoop(const Strings & files);
     void completePrefix(string prefix);
     bool getLine(string & input, const char * prompt);
+    Path getDerivationPath(Value & v);
     bool processLine(string line);
     void loadFile(const Path & path);
     void initEnv();
@@ -60,8 +70,35 @@ struct NixRepl
 
 void printHelp()
 {
-    std::cout << "Usage: nix-repl [--help|--version]";
-    std::cout << std::endl;
+    cout << "Usage: nix-repl [--help] [--version] [-I path] paths...\n"
+         << "\n"
+         << "nix-repl is a simple read-eval-print loop (REPL) for the Nix package manager.\n"
+         << "\n"
+         << "Options:\n"
+         << "    --help\n"
+         << "        Prints out a summary of the command syntax and exits.\n"
+         << "\n"
+         << "    --version\n"
+         << "        Prints out the Nix version number on standard output and exits.\n"
+         << "\n"
+         << "    -I path\n"
+         << "        Add a path to the Nix expression search path. This option may be given\n"
+         << "        multiple times. See the NIX_PATH environment variable for information on\n"
+         << "        the semantics of the Nix search path. Paths added through -I take\n"
+         << "        precedence over NIX_PATH.\n"
+         << "\n"
+         << "    paths...\n"
+         << "        A list of paths to files containing Nix expressions which nix-repl will\n"
+         << "        load and add to its scope.\n"
+         << "\n"
+         << "        A path surrounded in < and > will be looked up in the Nix expression search\n"
+         << "        path, as in the Nix language itself.\n"
+         << "\n"
+         << "        If an element of paths starts with http:// or https://, it is interpreted\n"
+         << "        as the URL of a tarball that will be downloaded and unpacked to a temporary\n"
+         << "        location. The tarball must include a single top-level directory containing\n"
+         << "        at least a file named default.nix.\n"
+         << flush;
 }
 
 
@@ -74,8 +111,8 @@ string removeWhitespace(string s)
 }
 
 
-NixRepl::NixRepl(const Strings & searchPath)
-    : state(searchPath)
+NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store)
+    : state(searchPath, store)
     , staticEnv(false, &state.staticBaseEnv)
 {
     curDir = absPath(".");
@@ -84,6 +121,7 @@ NixRepl::NixRepl(const Strings & searchPath)
 
 void NixRepl::mainLoop(const Strings & files)
 {
+    string error = ANSI_RED "error:" ANSI_NORMAL " ";
     std::cout << "Welcome to Nix version " << NIX_VERSION << ". Type :? for help." << std::endl << std::endl;
 
     for (auto & i : files)
@@ -109,19 +147,19 @@ void NixRepl::mainLoop(const Strings & files)
         }
 
         try {
-            if (!processLine(input)) return;
+            if (!removeWhitespace(input).empty() && !processLine(input)) return;
         } catch (ParseError & e) {
             if (e.msg().find("unexpected $end") != 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;
             } else {
-                printMsg(lvlError, "error: " + e.msg());
+              printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
             }
         } catch (Error & e) {
-            printMsg(lvlError, "error: " + e.msg());
+            printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
         } catch (Interrupted & e) {
-            printMsg(lvlError, "error: " + e.msg());
+            printMsg(lvlError, format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
         }
 
         // We handled the current input fully, so we should clear it and read brand new input.
@@ -181,14 +219,13 @@ bool NixRepl::getLine(string & input, const char * prompt)
 
         char * s = readline(prompt);
         if (!s) return false;
-        string line = chomp(string(s));
-        input.append(removeWhitespace(line));
+        input.append(s);
         input.push_back('\n');
-        free(s);
-        if (line != "") {
-            add_history(line.c_str());
+        if (!removeWhitespace(s).empty()) {
+            add_history(s);
             append_history(1, 0);
         }
+        free(s);
     }
 
     _isInterrupted = 0;
@@ -261,7 +298,7 @@ static int runProgram(const string & program, const Strings & args)
         _exit(1);
     }
 
-    return pid.wait(true);
+    return pid.wait();
 }
 
 
@@ -280,6 +317,17 @@ bool isVarName(const string & s)
 }
 
 
+Path NixRepl::getDerivationPath(Value & v) {
+    DrvInfo drvInfo(state);
+    if (!getDerivation(state, v, drvInfo, false))
+        throw Error("expression does not evaluate to a derivation, so I can't build it");
+    Path drvPath = drvInfo.queryDrvPath();
+    if (drvPath == "" || !state.store->isValidPath(drvPath))
+        throw Error("expression did not evaluate to a valid derivation");
+    return drvPath;
+}
+
+
 bool NixRepl::processLine(string line)
 {
     if (line == "") return true;
@@ -301,12 +349,14 @@ bool NixRepl::processLine(string line)
              << "  <x> = <expr>  Bind expression to variable\n"
              << "  :a <expr>     Add attributes from resulting set to scope\n"
              << "  :b <expr>     Build derivation\n"
+             << "  :i <expr>     Build derivation, then install result into current profile\n"
              << "  :l <path>     Load Nix expression and add it to scope\n"
              << "  :p <expr>     Evaluate and print expression recursively\n"
              << "  :q            Exit nix-repl\n"
              << "  :r            Reload all files\n"
              << "  :s <expr>     Build dependencies of derivation, then start nix-shell\n"
-             << "  :t <expr>     Describe result of evaluation\n";
+             << "  :t <expr>     Describe result of evaluation\n"
+             << "  :u <expr>     Build derivation, then start nix-shell\n";
     }
 
     else if (command == ":a" || command == ":add") {
@@ -329,17 +379,21 @@ bool NixRepl::processLine(string line)
         Value v;
         evalString(arg, v);
         std::cout << showType(v) << std::endl;
+
+    } else if (command == ":u") {
+        Value v, f, result;
+        evalString(arg, v);
+        evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
+        state.callFunction(f, v, result, Pos());
+
+        Path drvPath = getDerivationPath(result);
+        runProgram("nix-shell", Strings{drvPath});
     }
 
-    else if (command == ":b" || command == ":s") {
+    else if (command == ":b" || command == ":i" || command == ":s") {
         Value v;
         evalString(arg, v);
-        DrvInfo drvInfo(state);
-        if (!getDerivation(state, v, drvInfo, false))
-            throw Error("expression does not evaluation to a derivation, so I can't build it");
-        Path drvPath = drvInfo.queryDrvPath();
-        if (drvPath == "" || !store->isValidPath(drvPath))
-            throw Error("expression did not evaluate to a valid derivation");
+        Path drvPath = getDerivationPath(v);
 
         if (command == ":b") {
             /* We could do the build in this process using buildPaths(),
@@ -351,8 +405,11 @@ bool NixRepl::processLine(string line)
                 for (auto & i : drv.outputs)
                     std::cout << format("  %1% -> %2%") % i.first % i.second.path << std::endl;
             }
-        } else
+        } else if (command == ":i") {
+            runProgram("nix-env", Strings{"-i", drvPath});
+        } else {
             runProgram("nix-shell", Strings{drvPath});
+        }
     }
 
     else if (command == ":p" || command == ":print") {
@@ -499,23 +556,25 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
     switch (v.type) {
 
     case tInt:
-        str << v.integer;
+        str << ESC_CYA << v.integer << ESC_END;
         break;
 
     case tBool:
-        str << (v.boolean ? "true" : "false");
+        str << ESC_CYA << (v.boolean ? "true" : "false") << ESC_END;
         break;
 
     case tString:
+        str << ESC_YEL;
         printStringValue(str, v.string.s);
+        str << ESC_END;
         break;
 
     case tPath:
-        str << v.path; // !!! escaping?
+        str << ESC_GRE << v.path << ESC_END; // !!! escaping?
         break;
 
     case tNull:
-        str << "null";
+        str << ESC_CYA "null" ESC_END;
         break;
 
     case tAttrs: {
@@ -568,7 +627,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
                     try {
                         printValue(str, *i.second, maxDepth - 1, seen);
                     } catch (AssertionError & e) {
-                        str << "«error: " << e.msg() << "»";
+                        str << ESC_RED "«error: " << e.msg() << "»" ESC_END;
                     }
                 str << "; ";
             }
@@ -594,7 +653,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
                     try {
                         printValue(str, *v.listElems()[n], maxDepth - 1, seen);
                     } catch (AssertionError & e) {
-                        str << "«error: " << e.msg() << "»";
+                        str << ESC_RED "«error: " << e.msg() << "»" ESC_END;
                     }
                 str << " ";
             }
@@ -603,20 +662,23 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
         str << "]";
         break;
 
-    case tLambda:
-        str << "«lambda»";
+    case tLambda: {
+        std::ostringstream s;
+        s << v.lambda.fun->pos;
+        str << ESC_BLU "«lambda @ " << filterANSIEscapes(s.str()) << "»" ESC_END;
         break;
+    }
 
     case tPrimOp:
-        str << "«primop»";
+        str << ESC_MAG "«primop»" ESC_END;
         break;
 
     case tPrimOpApp:
-        str << "«primop-app»";
+        str << ESC_BLU "«primop-app»" ESC_END;
         break;
 
     default:
-        str << "«unknown»";
+        str << ESC_RED "«unknown»" ESC_END;
         break;
     }
 
@@ -649,8 +711,7 @@ int main(int argc, char * * argv)
             return true;
         });
 
-        store = openStore();
-        NixRepl repl(searchPath);
+        NixRepl repl(searchPath, openStore());
         repl.mainLoop(files);
 
         write_history(historyFile.c_str());