about summary refs log tree commit diff
path: root/nix-repl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'nix-repl.cc')
-rw-r--r--nix-repl.cc96
1 files changed, 60 insertions, 36 deletions
diff --git a/nix-repl.cc b/nix-repl.cc
index 1b098405fc82..8c4f4017470d 100644
--- a/nix-repl.cc
+++ b/nix-repl.cc
@@ -19,9 +19,6 @@ using namespace std;
 using namespace nix;
 
 
-string programId = "nix-repl";
-
-
 struct NixRepl
 {
     string curDir;
@@ -38,10 +35,10 @@ 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 & line);
+    bool getLine(string & input, const char * prompt);
     bool processLine(string line);
     void loadFile(const Path & path);
     void initEnv();
@@ -100,13 +97,11 @@ 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(".");
-
-    store = openStore();
 }
 
 
@@ -123,21 +118,35 @@ void NixRepl::mainLoop(const Strings & files)
     using_history();
     read_history(0);
 
+    string input;
+
     while (true) {
-        string line;
-        if (!getLine(line)) {
+        // When continuing input from previous lines, don't print a prompt, just align to the same
+        // number of chars as the prompt.
+        const char * prompt = input.empty() ? "nix-repl> " : "          ";
+        if (!getLine(input, prompt)) {
             std::cout << std::endl;
             break;
         }
 
         try {
-            if (!processLine(removeWhitespace(line))) 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());
+            }
         } catch (Error & e) {
             printMsg(lvlError, "error: " + e.msg());
         } catch (Interrupted & e) {
             printMsg(lvlError, "error: " + e.msg());
         }
 
+        // We handled the current input fully, so we should clear it and read brand new input.
+        input.clear();
         std::cout << std::endl;
     }
 }
@@ -176,7 +185,7 @@ char * completerThunk(const char * s, int state)
 }
 
 
-bool NixRepl::getLine(string & line)
+bool NixRepl::getLine(string & input, const char * prompt)
 {
     struct sigaction act, old;
     act.sa_handler = sigintHandler;
@@ -185,20 +194,21 @@ bool NixRepl::getLine(string & line)
     if (sigaction(SIGINT, &act, &old))
         throw SysError("installing handler for SIGINT");
 
-    if (sigsetjmp(sigintJmpBuf, 1))
-        line = "";
-    else {
+    if (sigsetjmp(sigintJmpBuf, 1)) {
+        input.clear();
+    } else {
         curRepl = this;
         rl_completion_entry_function = completerThunk;
 
-        char * s = readline("nix-repl> ");
+        char * s = readline(prompt);
         if (!s) return false;
-        line = chomp(string(s));
-        free(s);
-        if (line != "") {
-            add_history(line.c_str());
+        input.append(s);
+        input.push_back('\n');
+        if (!removeWhitespace(s).empty()) {
+            add_history(s);
             append_history(1, 0);
         }
+        free(s);
     }
 
     _isInterrupted = 0;
@@ -297,7 +307,7 @@ bool NixRepl::processLine(string line)
     string command, arg;
 
     if (line[0] == ':') {
-        size_t p = line.find(' ');
+        size_t p = line.find_first_of(" \n\r\t");
         command = string(line, 0, p);
         if (p != string::npos) arg = removeWhitespace(string(line, p));
     } else {
@@ -311,6 +321,7 @@ 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"
@@ -341,14 +352,14 @@ bool NixRepl::processLine(string line)
         std::cout << showType(v) << std::endl;
     }
 
-    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");
+            throw Error("expression does not evaluate to a derivation, so I can't build it");
         Path drvPath = drvInfo.queryDrvPath();
-        if (drvPath == "" || !store->isValidPath(drvPath))
+        if (drvPath == "" || !state.store->isValidPath(drvPath))
             throw Error("expression did not evaluate to a valid derivation");
 
         if (command == ":b") {
@@ -361,8 +372,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") {
@@ -485,6 +499,19 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
 }
 
 
+std::ostream & printStringValue(std::ostream & str, const char * string) {
+    str << "\"";
+    for (const char * i = string; *i; i++)
+        if (*i == '\"' || *i == '\\') str << "\\" << *i;
+        else if (*i == '\n') str << "\\n";
+        else if (*i == '\r') str << "\\r";
+        else if (*i == '\t') str << "\\t";
+        else str << *i;
+    str << "\"";
+    return str;
+}
+
+
 // FIXME: lot of cut&paste from Nix's eval.cc.
 std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen)
 {
@@ -504,14 +531,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
         break;
 
     case tString:
-        str << "\"";
-        for (const char * i = v.string.s; *i; i++)
-            if (*i == '\"' || *i == '\\') str << "\\" << *i;
-            else if (*i == '\n') str << "\\n";
-            else if (*i == '\r') str << "\\r";
-            else if (*i == '\t') str << "\\t";
-            else str << *i;
-        str << "\"";
+        printStringValue(str, v.string.s);
         break;
 
     case tPath:
@@ -559,7 +579,11 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
             }
 
             for (auto & i : sorted) {
-                str << i.first << " = ";
+                if (isVarName(i.first))
+                    str << i.first;
+                else
+                    printStringValue(str, i.first.c_str());
+                str << " = ";
                 if (hidden.find(i.first) != hidden.end())
                     str << "«...»";
                 else if (seen.find(i.second) != seen.end())
@@ -649,7 +673,7 @@ int main(int argc, char * * argv)
             return true;
         });
 
-        NixRepl repl(searchPath);
+        NixRepl repl(searchPath, openStore());
         repl.mainLoop(files);
     });
 }