clojure-emacs/ac-nrepl

Exception when pulling the documentation for a namespace

tbatchelli opened this issue · 12 comments

When trying to complete something like clojure.set, the nrepl process will show this exception:

java.lang.ClassNotFoundException: clojure.set
 at java.net.URLClassLoader$1.run (URLClassLoader.java:202)
    java.security.AccessController.doPrivileged (AccessController.java:-2)
    java.net.URLClassLoader.findClass (URLClassLoader.java:190)
    clojure.lang.DynamicClassLoader.findClass (DynamicClassLoader.java:61)
    java.lang.ClassLoader.loadClass (ClassLoader.java:306)
    java.lang.ClassLoader.loadClass (ClassLoader.java:247)
    java.lang.Class.forName0 (Class.java:-2)
    java.lang.Class.forName (Class.java:247)
    clojure.lang.RT.classForName (RT.java:2039)
    clojure.lang.Compiler.maybeResolveIn (Compiler.java:6789)
    clojure.core$ns_resolve.invoke (core.clj:3883)
    clojure.core$ns_resolve.invoke (core.clj:3880)
    clojure.core$resolve.invoke (core.clj:3889)
    clojure.repl$doc.invoke (repl.clj:128)
    clojure.lang.Var.invoke (Var.java:423)
    clojure.lang.AFn.applyToHelper (AFn.java:167)
    clojure.lang.Var.applyTo (Var.java:532)
    clojure.lang.Compiler.macroexpand1 (Compiler.java:6366)
    clojure.lang.Compiler.macroexpand (Compiler.java:6427)
    clojure.lang.Compiler.eval (Compiler.java:6495)
    clojure.lang.Compiler.eval (Compiler.java:6477)
    clojure.core$eval.invoke (core.clj:2797)
    clojure.main$repl$read_eval_print__6405.invoke (main.clj:245)
    clojure.main$repl$fn__6410.invoke (main.clj:266)
    clojure.main$repl.doInvoke (main.clj:266)
    clojure.lang.RestFn.invoke (RestFn.java:1096)
    clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn__609.invoke (interruptible_eval.clj:51)
    clojure.lang.AFn.applyToHelper (AFn.java:159)
    clojure.lang.AFn.applyTo (AFn.java:151)
    clojure.core$apply.invoke (core.clj:601)
    clojure.core$with_bindings_STAR_.doInvoke (core.clj:1771)
    clojure.lang.RestFn.invoke (RestFn.java:425)
    clojure.tools.nrepl.middleware.interruptible_eval$evaluate.invoke (interruptible_eval.clj:36)
    clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn__646$fn__648.invoke (interruptible_eval.clj:162)
    clojure.core$comp$fn__4034.invoke (core.clj:2278)
    clojure.tools.nrepl.middleware.interruptible_eval$run_next$fn__639.invoke (interruptible_eval.clj:129)
    clojure.lang.AFn.run (AFn.java:24)
    java.util.concurrent.ThreadPoolExecutor$Worker.runTask (ThreadPoolExecutor.java:886)
    java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:908)
    java.lang.Thread.run (Thread.java:680)

That's a known error upstream in nrepl (on the Clojure side). It will be fixed in time, but not by ac-nrepl.

it looks like this is actually an issue in clojure itself, as (doc clojure.set) returns the exception above, see http://dev.clojure.org/jira/browse/CLJ-902 which seems to be fixed for clojure 1.5

One option would be to filter out exceptions returned by doc and present nil (or ERROR) to the user.

That's not so easy, because the error is a compiler error, and intercepting errors thrown at the nrepl evaluation level is tricky.

Another option might be to evaluate code like that used by swank-clojure rather than relying on the possibly-broken clojure.repl/doc fn:

https://github.com/technomancy/swank-clojure/blob/master/src/swank/commands/basic.clj#L231

wrn commented

I am puzzled why if I do this in a regular "lein repl" shell:
(complete.core/completions "clojure.set/" ns)
everything works fine. I can see all the completions.

But if I do the same in an Emacs nrepl buffer, I got the exception as described in this thread.

I am using lein2 preview 10, and nrepl.el 0.1.4 preview

Can someone explain? Thanks.

@wrn To make a short story long, the reason why it works in REPL-y and not in nrepl.el is that the command line version of REPL-y installs a newer version of the function resolve-class in clojure-complete.

You can see this here:
https://github.com/trptcolin/reply/blob/master/src/clj/reply/initialization.clj#L129

We don't do this when launching the nrepl server.

This particular bug has been fixed in clojure-complete (see ninjudd/clojure-complete#3 ).
So getting it fixed is a matter of getting a new version of clojure-complete released and included in lein repl.

wrn commented

Thanks. That explains it. I just re-defined the resolve-class in namespace complete.core and completions works in nrepl buffer now. So I guess I can use this as a temporary work around until the new version is out.

Hi folks,

Even when redefining resolve-class in namespace complete.core, I still get the above-mentioned ClassNotFoundException from nrepl in emacs. It's actually coming from the call to clojure.repl/doc on line 82 of ac-nrepl.el:

https://github.com/purcell/ac-nrepl/blob/master/ac-nrepl.el#L82

Redefining ac-nrepl-documentation as

(defun ac-nrepl-documentation (symbol)
  "Return documentation for the given SYMBOL, if available."
  (substring-no-properties
   (replace-regexp-in-string
    "\r" ""
    (replace-regexp-in-string
     "^\\(  \\|-------------------------\r?\n\\)" ""
     (plist-get (nrepl-send-string-sync
                 (format "(try (eval '(clojure.repl/doc %s)) (catch Exception e (println \"Doc threw an error. Namespace?\")))" symbol)
                 (nrepl-current-ns))
                :stdout)))))

Fixes the problem. A similar issue exists in nrepl.el (https://github.com/kingtim/nrepl.el/blob/master/nrepl.el#L1486).

Does no-one else see this problem? I'll happily create pull requests if it would be useful.

Cheers,
Ian

I believe the issue here is possibly the use (nrepl-current-ns). I believe this should be nrepl-buffer-ns.
Which version of nrepl.el are you using?
Is this occurring while editing a clojure buffer, or in the repl buffer?
If a clojure buffer, have you loaded the file using C-c C-k?

Yes, nrepl-buffer-ns sounds right. Not sure where the (nrepl-current-ns) came from in the first place, but it's trivial for me to change it if it's obviously wrong...

I'm using the *nrepl* buffer. Whether or not there's an issue with (nrepl-current-ns), it is definitely the case that (clojure.repl/doc clojure.set) will always throw a ClassNotFoundException, which needs to be caught (at least until we all move to clojure 1.5, where the doc macro has been fixed).

I'm using the latest nrepl.el from the master branch.

I'm sure I'm missing something, I appreciate you folks taking the time to help!

Cheers,
Ian

I see.
Yes, it would appear we are going to have to wrap that doc call in a try catch.

Thanks guys -- I've added the fix from @daviesian and changed nrepl-current-ns to nrepl-buffer-ns. There's a new package (0.7) up on Marmalade.