rksm / Clj Suitable
Programming Languages
Projects that are alternatives of or similar to Clj Suitable
suitable - ClojureScript Completion Toolkit
suitable provides static and dynamic code completion for ClojureScript tools.
It integrates a) with the CLJS analyzer and using the compilation state for "static" symbol completion. This functionality was formerly part of compliment.
It b) can use a CLJS repl session to query and inspect JavaScript runtime state, allowing code completion for JavaScript objects and interfaces.
TODO: describe static completion / compliment feature
Dynamic code completion for CLJS repls
The dynamic code completion features allow for exploratory development by
inspecting the runtime. For example you work with DOM objects but can't remember
how to query for child elements. Type (.| js/document)
(with |
marking the
postion of your cursor) and press TAB. Methods and properties of js/document
will appear — including querySelector
and querySelectorAll
.
Dynamic code completion: BE AWARE OF THE SIDE EFFECTS
The dynamic code completion evaluates code on completion requests! It does
this by trying to enumerate the
properties
of JavaScript objects, so in the example above it would fetch all properties of
the js/document
object. This might cause side effects: Even just reading property values of an object can run arbitrary code if that object defines getter functions.
Moreover, also chains of methods and properties will be evaluated upon
completion requests. So for example, asking for completions for the code /
cursor position (-> js/some-object (.deleteAllMyFilesAndStartAWar) .|)
will
evaluate the JavaScript code some-object.deleteAllMyFilesAndStartAWar()
!
This only applies to JavaSript interop code, i.e. JavaScript methods and properties. Pure ClojureScript is not inspected or evaluated. Please be aware of this behavior when using the dynamic code completion features.
Dynamic completion Demo
The animation shows how various properties and methods of the native DOM can be accessed (Tab is used to show completions for the expression at the cursor):
Setup
figwheel.main with rebel-readline
Please note that you need to use rebel-readline with figwheel for that to work. Plain repls have no completion feature.
Tools CLI
First make sure that the normal Tools CLI setup works.
Then modify deps.edn
and dev.cljs.edn
, you should end up with the files looking like below:
deps.edn
{:deps {com.bhauman/figwheel-main {:mvn/version "RELEASE"}
com.bhauman/rebel-readline-cljs {:mvn/version "RELEASE"}}
:paths ["src" "resources" "target"]
:aliases {:suitable {:extra-deps {org.rksm/suitable {:mvn/version "RELEASE"}}
:main-opts ["-e" "(require,'suitable.hijack-rebel-readline-complete)"
"-m" "figwheel.main"
"--build" "dev" "--repl"]}}}
dev.cljs.edn
{:main example.core
:preloads [suitable.js-introspection]}
src/example/core.cljs
(ns example.core)
You can now start a figwheel repl via clj -A:suitable
and use TAB to complete.
leiningen
First make sure that the normal leiningen setup works.
Add [org.rksm/suitable "0.3.5"]
to your dependencies vector.
Then you can start a repl with lein trampoline run -m suitable.figwheel.main -- -b dev -r
Emacs CIDER
Suitable is now a default middleware in CIDER (as of CIDER 0.22.0)! So no extra installation steps are required.
Custom nREPL server
To load suitable into a custom server you can load it using this monstrosity:
clj -Sdeps '{:deps {cider/cider-nrepl {:mvn/version "0.22.0"} cider/piggieback {:mvn/version"0.4.1"}}}' -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware,cider.piggieback/wrap-cljs-repl]"
Or from within Clojure:
(ns my-own-nrepl-server
(:require cider.nrepl
cider.piggieback
nrepl.server))
(defn start-cljs-nrepl-server []
(let [middlewares (conj (map resolve cider.nrepl/cider-middleware)
#'cider.piggieback/wrap-cljs-repl)
handler (apply nrepl.server/default-handler middlewares)]
(nrepl.server/start-server :handler handler))
shadow-cljs + cider
Please see issue #2.
How does it work?
suitable uses the same input as the widely used
compliment. This means it
gets a prefix string and a context form from the tool it is connected to. For
example you type (.l| js/console)
with "|" marking where your cursor is. The
input we get would then be: prefix = .l
and context = (__prefix__ js/console)
.
suitable recognizes various ways how CLJS can access properties and methods,
such as .
, ..
, doto
, and threading forms. Also direct global access is
supported such as js/console.log
. suitable will then figure out the expression
that defines the "parent object" that the property / method we want to use
belongs to. For the example above it would be js/console
. The system then uses
the EcmaScript property descriptor API
to enumerate the object members. Those are converted into completion candidates
and send back to the tooling.
License
This project is MIT licensed.