Skip to content

Commit

Permalink
[babashka#30] add nrepl info and lookup operation support
Browse files Browse the repository at this point in the history
Adds handling of :lookup and :info operations, so that IDEs can offer
function parameters and documentation strings of functions.
  • Loading branch information
brdloush committed Mar 28, 2021
1 parent 15ff3c1 commit d300064
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 25 deletions.
66 changes: 43 additions & 23 deletions src/babashka/nrepl/impl/server.clj
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,10 @@
completions (keep (fn [entry]
(match alias->ns ns->alias query entry))
svs)
completions (mapv (fn [[namespace name]]
{"candidate" (str name) "ns" (str namespace) #_"type" #_"function"})
completions)]
completions (->> (map (fn [[namespace name]]
{"candidate" (str name) "ns" (str namespace) #_"type" #_"function"})
completions)
set)]
(when debug (prn "completions" completions))
(utils/send o (utils/response-for msg {"completions" completions
"status" #{"done"}}) opts))
Expand All @@ -141,34 +142,50 @@
(utils/send os (utils/response-for msg {"sessions" sessions
"status" #{"done"}}) opts)))

(defn eldoc [ctx msg os {:keys [debug] :as opts}]
(defn lookup [ctx msg os mapping-type {:keys [debug] :as opts}]
(let [ns-str (:ns msg)
sym-str (:sym msg)
sym-str (or (:sym msg) (:symbol msg))
sym-str-ns-striped (last (str/split sym-str #"\/"))
sci-ns (when ns-str
(sci-utils/namespace-object (:env ctx) (symbol ns-str) nil false))]
(try
(sci/binding [vars/current-ns (or sci-ns @vars/current-ns)]
(let [m (sci/eval-string* ctx (format "
(when-let [v (ns-resolve '%s '%s)]
(let [m (meta v)]
(assoc m :arglists (:arglists m)
:doc (:doc m)
:name (:name m)
:ns (some-> m :ns ns-name)
:val @v)))" ns-str sym-str))
reply {"ns" (:ns m)
"name" (:name m)
"eldoc" (mapv #(mapv str %) (:arglists m))
"type" (cond
(ifn? (:val m)) "function"
:else "variable")
"docstring" (:doc m)
"status" #{"done"}}]
(let [sym-ns-str '%s
sym-str '%s
ns-striped-sym-str '%s]
(when-let [v (or (ns-resolve sym-ns-str sym-str)
(ns-resolve sym-ns-str ns-striped-sym-str))]
(let [m (meta v)]
(assoc m :arglists (:arglists m)
:doc (:doc m)
:name (:name m)
:ns (some-> m :ns ns-name)
:val @v))))" ns-str sym-str sym-str-ns-striped))
arglists-vec (mapv #(mapv str %) (:arglists m))
reply (->> (case mapping-type
:eldoc {"ns" (:ns m)
"name" (:name m)
"eldoc" arglists-vec
"docstring" (:doc m)
"type" (cond
(ifn? (:val m)) "function"
:else "variable")
"status" #{"done"}}
:lookup {"ns" (:ns m)
"name" (:name m)
"arglists-str" (str arglists-vec)
"status" #{"done"}
"doc" (:doc m)})
(remove #(nil? (second %)))
(into {}))]
(utils/send os
(utils/response-for msg reply) opts)))
(catch Throwable e
(when debug (println e))
(utils/send os (utils/response-for msg {"status" #{"done" "no-eldoc"}}) opts)))))
(let [status (remove nil? #{"done"
(when (= mapping-type :eldoc) "no-eldoc")})]
(utils/send os (utils/response-for msg {"status" status}) opts))))))

(defn read-msg [msg]
(-> (zipmap (map keyword (keys msg))
Expand Down Expand Up @@ -207,22 +224,25 @@
:complete (do
(complete ctx os msg opts)
(recur ctx is os id opts))
(:lookup :info) (do
(lookup ctx msg os :lookup opts)
(recur ctx is os id opts))
:describe
(do (utils/send os (utils/response-for
msg
(merge-with merge
{"status" #{"done"}
"ops" (zipmap #{"clone" "close" "eval" "load-file"
"complete" "describe" "ls-sessions"
"eldoc"}
"eldoc" "info" "lookup"}
(repeat {}))
"versions" {"babashka.nrepl" babashka-nrepl-version}}
(:describe opts))) opts)
(recur ctx is os id opts))
:ls-sessions (do (ls-sessions ctx msg os opts)
(recur ctx is os id opts))
:eldoc (do
(eldoc ctx msg os opts)
(lookup ctx msg os :eldoc opts)
(recur ctx is os id opts))
;; fallback
(do (when debug
Expand Down
3 changes: 2 additions & 1 deletion src/babashka/nrepl/impl/utils.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
{:author "Michiel Borkent"
:no-doc true}
(:refer-clojure :exclude [send])
(:require [bencode.core :refer [write-bencode]])
(:require [bencode.core :refer [write-bencode]]
[clojure.string :as str])
(:import [java.io Writer PrintWriter StringWriter OutputStream BufferedWriter]))

(set! *warn-on-reflection* true)
Expand Down
36 changes: 35 additions & 1 deletion test/babashka/nrepl/server_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,43 @@
(is (= "variable" type))))
(testing "eldoc of invalid characters"
(bencode/write-bencode os {"op" "eldoc" "ns" "user"
"sym" ""
"sym" "\r"
"session" session "id" (new-id!)})
(let [{:keys [status]} (read-reply in session @id)]
(is (contains? (set status) "no-eldoc")))))
(testing "lookup"
(testing "lookup of inc"
(bencode/write-bencode os {"op" "lookup" "ns" "user"
"symbol" "inc"
"session" session "id" (new-id!)})
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
(is (str/includes? doc "Returns a number one greater than num"))
(is (= "[[\"x\"]]" arglists-str))))
(testing "lookup of last-index-of"
(bencode/write-bencode os {"op" "lookup" "ns" "user"
"symbol" "clojure.string/last-index-of"
"session" session "id" (new-id!)})
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
(is (str/includes? doc "Return last index of value (string or char) in s"))
(is (= "[[\"s\" \"value\"] [\"s\" \"value\" \"from-index\"]]" arglists-str))))
(testing "lookup of s/lower-case (from core ns, aliased as s/, core passed as ns)"
(bencode/write-bencode os {"op" "eval" "code" "(ns core)
(require '[clojure.string :as s])" "session" session "id" (new-id!)})
(bencode/write-bencode os {"op" "lookup" "ns" "core"
"symbol" "s/lower-case"
"session" session "id" (new-id!)})
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
(is (str/includes? doc "Converts string to all lower-case"))
(is (= "[[\"s\"]]" arglists-str))))
(testing "lookup of s/lower-case (from core ns, aliased as s/, clojure.string passed as ns)"
(bencode/write-bencode os {"op" "eval" "code" "(ns core)
(require '[clojure.string :as s])" "session" session "id" (new-id!)})
(bencode/write-bencode os {"op" "lookup" "ns" "clojure.string"
"symbol" "s/lower-case"
"session" session "id" (new-id!)})
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
(is (str/includes? doc "Converts string to all lower-case"))
(is (= "[[\"s\"]]" arglists-str)))))
(testing "dynamic var can be set! if provided in :dynamic-vars option"
(bencode/write-bencode os {"op" "eval" "code" "(set! *warn-on-reflection* true)"
"session" session "id" (new-id!)})
Expand Down Expand Up @@ -373,3 +406,4 @@

(comment
)

0 comments on commit d300064

Please sign in to comment.