Trick to getting bindings in a repl

16 views
Skip to first unread message

Mike

unread,
Dec 30, 2010, 11:34:51 AM12/30/10
to Clojure
I'm doing something kind of tricky...I'm running a REPL in my Java
Swing application. I do something like:

public class MyConsoleClass extends OurEmbeddedInterpreterClass {
// ...

// inner class
private class ReplRunner extends Thread {
public void run() {
Namespace MYNS = Namespace.findOrCreate(Symbol.create("myns"));
Var ACCESS_ME = Var.intern(MYNS, Symbol.create("*me*"));
Var.pushThreadBindings(RT.map(RT.IN, myIn, RT.OUT, myOut,
RT.ERR, myErr, ACCESS_ME, MyConsoleClass.this);
BufferedReader script = getMagicInitScript();
Compiler.load(script, getScriptPath(), getScriptFile());
}
}
// ...somewhere, make a ReplRunner and launch it when you're
supposed to
}

The "magic init script" is what runs the repl. The ins and outs are
mapped into Swing components so you can type commands, it passes to
the repl, and then output comes back to a text pane or the equivalent.

Now, my GUI app puts a KeyListener on the input component so that when
the TAB key is pressed, it runs a completion routine (Java callback)
to try to complete your command. I wrote a function (in Clojure,
inside the magic init script) that returns all the completions based
on the current command. My Java callback looks like:

// a method in MyConsoleClass...CodeCompletion is our own little
helper struct...
public List<CodeCompletion> getCompletions(String cmd) {
Var getFn = RT.var("myns", "get-completions");
try {
List<CodeCompletion> result = (List<CodeCompletion>)
getFn.invoke(cmd);
return result;
catch(Exception e) { /* handle e omitted */ }
return null;
}

The problem is, get-completions uses dir-fn and ns-map to gather
bindings based on the context of the command. But it's being invoked
in the AWT event thread (which calls this getCompletions method), NOT
the ReplRunner thread that I launch (which is of course just looping
infinitely waiting on input). So all the local bindings (including
*me* that I bound in ReplRunner.run()) are missing from the results.

Does anybody have any ideas how I can jack into the repl thread to
grab the bindings from it? I don't think any of Clojure's concurrency
constructs will help me here, because I need to interfere with a
thread that's essentially blocked at the time of the (synchronous)
completion call.

Sorry if this isn't fully specified and hurried...I can't paste the
code directly here because of NDA. If you need more details I can
provide them.

Thanks in advance for any ideas...
Mike

Chas Emerick

unread,
Jan 5, 2011, 3:02:56 PM1/5/11
to Clojure
Clojure's vars maintain bindings in ThreadLocals[1]. I'm pretty sure
accessing ThreadLocals of other threads can't be done. Looking at
ThreadLocal and Thread sources, you might be able to dig around
private and package-private fields to get at them. Generally a bad
idea.

FWIW, the Enclojure REPL is available as a standalone Swing
component[2], which may or may not be a good basis for your work.

Also FWIW, you might want to take a look at nREPL[3], which is an
attempt to provide a common network-capable Clojure REPL (currently
used by ccw and vimclojure AFAIK). The benefit for you there would be
that the thread-local state of each REPL connection (its "session",
for lack of a better term) can be queried at will, asynchronously from
code that your users might want to send to the REPL.

Cheers,

- Chas

[1] http://download.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
[2] http://www.enclojure.org/The+Enclojure+REPLs+%28Not+just+for+Netbeans!%29
[3] https://github.com/clojure/tools.nrepl
Reply all
Reply to author
Forward
0 new messages