Skip to content

Commit

Permalink
Improved autocompletion support
Browse files Browse the repository at this point in the history
  • Loading branch information
setzer22 committed Apr 9, 2020
1 parent 642e5e7 commit 8fc51b5
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 63 deletions.
14 changes: 8 additions & 6 deletions Editor/NRepl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -480,17 +480,19 @@ static void HandleMessage (BDictionary message, TcpClient client)
}, client);
break;
case "classpath":
// Debug.Log("Classpath op has been called");
string assetsPath = Path.Combine(Path.GetFullPath("."), "Assets");
string arcadiaSrcPath = Path.Combine(assetsPath, "Arcadia", "Source");
// Debug.Log("assetsPath: " + assetsPath);
// Debug.Log("arcadiaSrcPath: " + arcadiaSrcPath);
BList classpath = new BList();
foreach (String p in Environment.GetEnvironmentVariable("CLOJURE_LOAD_PATH").Split(System.IO.Path.PathSeparator)) {
if (p != "") {
classpath.Add(Path.GetFullPath(p));
}
}

SendMessage(new BDictionary
{
{"id", message["id"]},
{"session", session.ToString()},
{"status", new BList {"done"}},
{"classpath", new BList { assetsPath, arcadiaSrcPath }},
{"classpath", classpath},
}, client);
break;
default:
Expand Down
112 changes: 112 additions & 0 deletions Source/arcadia/internal/autocompletion.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
(ns arcadia.internal.autocompletion
(:require [clojure.main]))

;; This namespace has been adapted from a fork of the `clojure-complete` library by @sogaiu:
;; https://github.com/sogaiu/clojure-complete/blob/clr-support/src/complete/core.cljc
;; The original code was in turn adapted from swank-clojure (http://github.com/jochu/swank-clojure)
;; List of changes:
;; - Added support for keyword autocompletion
;; - Removed conditional reader tags

(defn namespaces
"Returns a list of potential namespace completions for a given namespace"
[ns]
(map name (concat (map ns-name (all-ns)) (keys (ns-aliases ns)))))

(defn ns-public-vars
"Returns a list of potential public var name completions for a given
namespace"
[ns]
(map name (keys (ns-publics ns))))

(defn ns-vars
"Returns a list of all potential var name completions for a given namespace"
[ns]
(for [[sym val] (ns-map ns) :when (var? val)]
(name sym)))

(defn ns-classes
"Returns a list of potential class name completions for a given namespace"
[ns]
(map name (keys (ns-imports ns))))

(def special-forms
(map name '[def if do let quote var fn loop recur throw try monitor-enter
monitor-exit dot new set!]))

(defn- static? [member]
(.IsStatic member))

(defn static-members
"Returns a list of potential static members for a given class"
[^System.RuntimeType class]
(for [member (concat (.GetMethods class)
(.GetFields class)
'()) :when (static? member)]
(.Name member)))

(defn resolve-class [sym]
(try (let [val (resolve sym)]
(when (class? val) val))
(catch Exception e
(when (not= clojure.lang.TypeNotFoundException
(class (clojure.main/repl-exception e)))
(throw e)))))

(defmulti potential-completions
(fn [^String prefix ns]
(cond (.Contains prefix "/") :scoped
(.Contains prefix ".") :class
(.StartsWith prefix ":") :keyword
:else :var)))

(defmethod potential-completions :scoped
[^String prefix ns]
(when-let [prefix-scope
(first (let [[x & _ :as pieces]
(.Split prefix (.ToCharArray "/"))]
(if (= x "")
'()
pieces)))]
(let [scope (symbol prefix-scope)]
(map #(str scope "/" %)
(if-let [class (resolve-class scope)]
(static-members class)
(when-let [ns (or (find-ns scope)
(scope (ns-aliases ns)))]
(ns-public-vars ns)))))))

(defmethod potential-completions :class
[^String prefix ns]
(concat (namespaces ns)))

(defmethod potential-completions :var
[_ ns]
(concat special-forms
(namespaces ns)
(ns-vars ns)
(ns-classes ns)))

(def sym-key-map
(-> clojure.lang.Keyword
(.GetField "_symKeyMap" (enum-or BindingFlags/NonPublic BindingFlags/Static))
(.GetValue nil)))

(defmethod potential-completions :keyword
[_ _]
(let [keyword-candidate-list
(->> sym-key-map
(.Values)
(map #(str (.Target %))))]
keyword-candidate-list))

(defn completions
"Return a sequence of matching completions given a prefix string and an
optional current namespace."
([prefix] (completions prefix *ns*))
([^String prefix ns]
(-> (for [^String completion (potential-completions prefix ns)
:when (.StartsWith completion prefix)]
completion)
distinct
sort)))
64 changes: 7 additions & 57 deletions Source/arcadia/internal/nrepl_support.clj
Original file line number Diff line number Diff line change
@@ -1,68 +1,18 @@
(ns arcadia.internal.nrepl-support
(:require [arcadia.internal.autocompletion :as ac])
(:import [BList]
[BDictionary]))

(defn complete-symbol [text]
(let [[ns prefix-str] (as-> text <>
(symbol <>)
[(some-> <> namespace symbol) (name <>)])
ns-to-check (if ns
(or ((ns-aliases *ns*) ns) (find-ns ns))
*ns*)
fn-candidate-list (when ns-to-check
(if ns
(map str (keys (ns-publics ns-to-check)))
(map str (keys (ns-map ns-to-check)))))]
(into '() (comp (filter #(.StartsWith % prefix-str))
(map #(if ns (str ns "/" %) %))
(map #(-> {:candidate %
:type "function"})))
(concat
fn-candidate-list))))

(defn complete-namespace [text]
(let [[ns prefix-str] (as-> text <>
(symbol <>)
[(some-> <> namespace symbol) (name <>)])
ns-candidate-list (when-not ns
(map (comp str ns-name) (all-ns)))]
(into '() (comp (filter #(.StartsWith % prefix-str))
(map str)
(map #(-> {:candidate %
:type "namespace"})))
ns-candidate-list)))

(def sym-key-map
(-> clojure.lang.Keyword
(.GetField "_symKeyMap" (enum-or BindingFlags/NonPublic BindingFlags/Static))
(.GetValue nil)))

(defn complete-keyword [text]
(let [keyword-candidate-list
;; NOTE: :_ is used here to get an instance to some keyword.
(->> sym-key-map
(.Values)
(map #(str (.Target %))))]
(into '() (comp (filter #(.StartsWith % text))
(map #(-> {:candidate %
:type "keyword"})))
keyword-candidate-list)))

(defn bencode-result
(defn bencode-completion-result
"Converts a seq of completion maps into a BList of BDictionary"
[completions]
(let [blist (BList.)]
(doseq [{:keys [candidate, type]} completions]
(doseq [candidate completions]
(.Add blist (doto (BDictionary.)
(.Add "candidate" candidate)
(.Add "type" type))))
(.Add "candidate" candidate))))
blist))

(defn complete [^String prefix]
(bencode-result
(cond
(.StartsWith prefix ":") (complete-keyword prefix)
(.Contains prefix "/") (complete-symbol prefix)
:else
(concat (complete-symbol prefix)
(complete-namespace prefix)))))
(bencode-completion-result
(ac/completions prefix)))

0 comments on commit 8fc51b5

Please sign in to comment.