about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nix-repl.cc129
1 files changed, 87 insertions, 42 deletions
diff --git a/nix-repl.cc b/nix-repl.cc
index 2ef23faa585a..f85eac1246b8 100644
--- a/nix-repl.cc
+++ b/nix-repl.cc
@@ -29,9 +29,15 @@ struct NixRepl
     StaticEnv staticEnv;
     Env * env;
     int displ;
+    StringSet varNames;
+
+    StringSet completions;
+    StringSet::iterator curCompletion;
 
     NixRepl();
     void mainLoop(const Strings & args);
+    void completePrefix(string prefix);
+    bool getLine(string & line);
     void processLine(string line);
     void loadFile(const Path & path);
     void addAttrsToScope(Value & attrs);
@@ -47,48 +53,6 @@ void printHelp()
 }
 
 
-/* Apparently, the only way to get readline() to return on Ctrl-C
-   (SIGINT) is to use siglongjmp().  That's fucked up... */
-static sigjmp_buf sigintJmpBuf;
-
-
-static void sigintHandler(int signo)
-{
-    siglongjmp(sigintJmpBuf, 1);
-}
-
-
-bool getLine(string & line)
-{
-    struct sigaction act, old;
-    act.sa_handler = sigintHandler;
-    sigfillset(&act.sa_mask);
-    act.sa_flags = 0;
-    if (sigaction(SIGINT, &act, &old))
-        throw SysError("installing handler for SIGINT");
-
-    if (sigsetjmp(sigintJmpBuf, 1))
-        line = "";
-    else {
-        char * s = readline("nix-repl> ");
-        if (!s) return false;
-        line = chomp(string(s));
-        free(s);
-        if (line != "") {
-            add_history(line.c_str());
-            append_history(1, 0);
-        }
-    }
-
-    _isInterrupted = 0;
-
-    if (sigaction(SIGINT, &old, 0))
-        throw SysError("restoring handler for SIGINT");
-
-    return true;
-}
-
-
 string removeWhitespace(string s)
 {
     s = chomp(s);
@@ -143,6 +107,86 @@ void NixRepl::mainLoop(const Strings & args)
 }
 
 
+/* Apparently, the only way to get readline() to return on Ctrl-C
+   (SIGINT) is to use siglongjmp().  That's fucked up... */
+static sigjmp_buf sigintJmpBuf;
+
+
+static void sigintHandler(int signo)
+{
+    siglongjmp(sigintJmpBuf, 1);
+}
+
+
+/* Oh, if only g++ had nested functions... */
+NixRepl * curRepl;
+
+char * completerThunk(const char * s, int state)
+{
+    string prefix(s);
+
+    /* If the prefix has a slash in it, use readline's builtin filename
+       completer. */
+    if (prefix.find('/') != string::npos)
+        return rl_filename_completion_function(s, state);
+
+    /* Otherwise, return all symbols that start with the prefix. */
+    if (state == 0) {
+        curRepl->completePrefix(s);
+        curRepl->curCompletion = curRepl->completions.begin();
+    }
+    if (curRepl->curCompletion == curRepl->completions.end()) return 0;
+    return strdup((curRepl->curCompletion++)->c_str());
+}
+
+
+bool NixRepl::getLine(string & line)
+{
+    struct sigaction act, old;
+    act.sa_handler = sigintHandler;
+    sigfillset(&act.sa_mask);
+    act.sa_flags = 0;
+    if (sigaction(SIGINT, &act, &old))
+        throw SysError("installing handler for SIGINT");
+
+    if (sigsetjmp(sigintJmpBuf, 1))
+        line = "";
+    else {
+        curRepl = this;
+        rl_completion_entry_function = completerThunk;
+
+        char * s = readline("nix-repl> ");
+        if (!s) return false;
+        line = chomp(string(s));
+        free(s);
+        if (line != "") {
+            add_history(line.c_str());
+            append_history(1, 0);
+        }
+    }
+
+    _isInterrupted = 0;
+
+    if (sigaction(SIGINT, &old, 0))
+        throw SysError("restoring handler for SIGINT");
+
+    return true;
+}
+
+
+void NixRepl::completePrefix(string prefix)
+{
+    completions.clear();
+
+    StringSet::iterator i = varNames.lower_bound(prefix);
+    while (i != varNames.end()) {
+        if (string(*i, 0, prefix.size()) != prefix) break;
+        completions.insert(*i);
+        i++;
+    }
+}
+
+
 void NixRepl::processLine(string line)
 {
     if (line == "") return;
@@ -227,6 +271,7 @@ void NixRepl::addVarToScope(const Symbol & name, Value * v)
 {
     staticEnv.vars[name] = displ;
     env->values[displ++] = v;
+    varNames.insert((string) name);
 }