Skip to content

Commit 6d1af42

Browse files
authored
[#30] add nrepl info and lookup operation support (#38)
Adds handling of :lookup and :info operations, so that IDEs can offer function parameters and documentation strings of functions.
1 parent 15ff3c1 commit 6d1af42

File tree

3 files changed

+80
-25
lines changed

3 files changed

+80
-25
lines changed

src/babashka/nrepl/impl/server.clj

+43-23
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@
119119
completions (keep (fn [entry]
120120
(match alias->ns ns->alias query entry))
121121
svs)
122-
completions (mapv (fn [[namespace name]]
123-
{"candidate" (str name) "ns" (str namespace) #_"type" #_"function"})
124-
completions)]
122+
completions (->> (map (fn [[namespace name]]
123+
{"candidate" (str name) "ns" (str namespace) #_"type" #_"function"})
124+
completions)
125+
set)]
125126
(when debug (prn "completions" completions))
126127
(utils/send o (utils/response-for msg {"completions" completions
127128
"status" #{"done"}}) opts))
@@ -141,34 +142,50 @@
141142
(utils/send os (utils/response-for msg {"sessions" sessions
142143
"status" #{"done"}}) opts)))
143144

144-
(defn eldoc [ctx msg os {:keys [debug] :as opts}]
145+
(defn lookup [ctx msg os mapping-type {:keys [debug] :as opts}]
145146
(let [ns-str (:ns msg)
146-
sym-str (:sym msg)
147+
sym-str (or (:sym msg) (:symbol msg))
148+
sym-str-ns-striped (last (str/split sym-str #"\/"))
147149
sci-ns (when ns-str
148150
(sci-utils/namespace-object (:env ctx) (symbol ns-str) nil false))]
149151
(try
150152
(sci/binding [vars/current-ns (or sci-ns @vars/current-ns)]
151153
(let [m (sci/eval-string* ctx (format "
152-
(when-let [v (ns-resolve '%s '%s)]
153-
(let [m (meta v)]
154-
(assoc m :arglists (:arglists m)
155-
:doc (:doc m)
156-
:name (:name m)
157-
:ns (some-> m :ns ns-name)
158-
:val @v)))" ns-str sym-str))
159-
reply {"ns" (:ns m)
160-
"name" (:name m)
161-
"eldoc" (mapv #(mapv str %) (:arglists m))
162-
"type" (cond
163-
(ifn? (:val m)) "function"
164-
:else "variable")
165-
"docstring" (:doc m)
166-
"status" #{"done"}}]
154+
(let [sym-ns-str '%s
155+
sym-str '%s
156+
ns-striped-sym-str '%s]
157+
(when-let [v (or (ns-resolve sym-ns-str sym-str)
158+
(ns-resolve sym-ns-str ns-striped-sym-str))]
159+
(let [m (meta v)]
160+
(assoc m :arglists (:arglists m)
161+
:doc (:doc m)
162+
:name (:name m)
163+
:ns (some-> m :ns ns-name)
164+
:val @v))))" ns-str sym-str sym-str-ns-striped))
165+
arglists-vec (mapv #(mapv str %) (:arglists m))
166+
reply (->> (case mapping-type
167+
:eldoc {"ns" (:ns m)
168+
"name" (:name m)
169+
"eldoc" arglists-vec
170+
"docstring" (:doc m)
171+
"type" (cond
172+
(ifn? (:val m)) "function"
173+
:else "variable")
174+
"status" #{"done"}}
175+
:lookup {"ns" (:ns m)
176+
"name" (:name m)
177+
"arglists-str" (str arglists-vec)
178+
"status" #{"done"}
179+
"doc" (:doc m)})
180+
(remove #(nil? (second %)))
181+
(into {}))]
167182
(utils/send os
168183
(utils/response-for msg reply) opts)))
169184
(catch Throwable e
170185
(when debug (println e))
171-
(utils/send os (utils/response-for msg {"status" #{"done" "no-eldoc"}}) opts)))))
186+
(let [status (remove nil? #{"done"
187+
(when (= mapping-type :eldoc) "no-eldoc")})]
188+
(utils/send os (utils/response-for msg {"status" status}) opts))))))
172189

173190
(defn read-msg [msg]
174191
(-> (zipmap (map keyword (keys msg))
@@ -207,22 +224,25 @@
207224
:complete (do
208225
(complete ctx os msg opts)
209226
(recur ctx is os id opts))
227+
(:lookup :info) (do
228+
(lookup ctx msg os :lookup opts)
229+
(recur ctx is os id opts))
210230
:describe
211231
(do (utils/send os (utils/response-for
212232
msg
213233
(merge-with merge
214234
{"status" #{"done"}
215235
"ops" (zipmap #{"clone" "close" "eval" "load-file"
216236
"complete" "describe" "ls-sessions"
217-
"eldoc"}
237+
"eldoc" "info" "lookup"}
218238
(repeat {}))
219239
"versions" {"babashka.nrepl" babashka-nrepl-version}}
220240
(:describe opts))) opts)
221241
(recur ctx is os id opts))
222242
:ls-sessions (do (ls-sessions ctx msg os opts)
223243
(recur ctx is os id opts))
224244
:eldoc (do
225-
(eldoc ctx msg os opts)
245+
(lookup ctx msg os :eldoc opts)
226246
(recur ctx is os id opts))
227247
;; fallback
228248
(do (when debug

src/babashka/nrepl/impl/utils.clj

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
{:author "Michiel Borkent"
33
:no-doc true}
44
(:refer-clojure :exclude [send])
5-
(:require [bencode.core :refer [write-bencode]])
5+
(:require [bencode.core :refer [write-bencode]]
6+
[clojure.string :as str])
67
(:import [java.io Writer PrintWriter StringWriter OutputStream BufferedWriter]))
78

89
(set! *warn-on-reflection* true)

test/babashka/nrepl/server_test.clj

+35-1
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,43 @@
336336
(is (= "variable" type))))
337337
(testing "eldoc of invalid characters"
338338
(bencode/write-bencode os {"op" "eldoc" "ns" "user"
339-
"sym" ""
339+
"sym" "\r"
340340
"session" session "id" (new-id!)})
341341
(let [{:keys [status]} (read-reply in session @id)]
342342
(is (contains? (set status) "no-eldoc")))))
343+
(testing "lookup"
344+
(testing "lookup of inc"
345+
(bencode/write-bencode os {"op" "lookup" "ns" "user"
346+
"symbol" "inc"
347+
"session" session "id" (new-id!)})
348+
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
349+
(is (str/includes? doc "Returns a number one greater than num"))
350+
(is (= "[[\"x\"]]" arglists-str))))
351+
(testing "lookup of last-index-of"
352+
(bencode/write-bencode os {"op" "lookup" "ns" "user"
353+
"symbol" "clojure.string/last-index-of"
354+
"session" session "id" (new-id!)})
355+
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
356+
(is (str/includes? doc "Return last index of value (string or char) in s"))
357+
(is (= "[[\"s\" \"value\"] [\"s\" \"value\" \"from-index\"]]" arglists-str))))
358+
(testing "lookup of s/lower-case (from core ns, aliased as s/, core passed as ns)"
359+
(bencode/write-bencode os {"op" "eval" "code" "(ns core)
360+
(require '[clojure.string :as s])" "session" session "id" (new-id!)})
361+
(bencode/write-bencode os {"op" "lookup" "ns" "core"
362+
"symbol" "s/lower-case"
363+
"session" session "id" (new-id!)})
364+
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
365+
(is (str/includes? doc "Converts string to all lower-case"))
366+
(is (= "[[\"s\"]]" arglists-str))))
367+
(testing "lookup of s/lower-case (from core ns, aliased as s/, clojure.string passed as ns)"
368+
(bencode/write-bencode os {"op" "eval" "code" "(ns core)
369+
(require '[clojure.string :as s])" "session" session "id" (new-id!)})
370+
(bencode/write-bencode os {"op" "lookup" "ns" "clojure.string"
371+
"symbol" "s/lower-case"
372+
"session" session "id" (new-id!)})
373+
(let [{:keys [doc arglists-str]} (read-reply in session @id)]
374+
(is (str/includes? doc "Converts string to all lower-case"))
375+
(is (= "[[\"s\"]]" arglists-str)))))
343376
(testing "dynamic var can be set! if provided in :dynamic-vars option"
344377
(bencode/write-bencode os {"op" "eval" "code" "(set! *warn-on-reflection* true)"
345378
"session" session "id" (new-id!)})
@@ -373,3 +406,4 @@
373406

374407
(comment
375408
)
409+

0 commit comments

Comments
 (0)