• Stars
    star
    114
  • Rank 308,031 (Top 7 %)
  • Language
    Clojure
  • License
    MIT License
  • Created over 5 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

ClojureScript "IntelliSense" support for JS objects and their properties/methods. Via figwheel and Emacs CIDER.

suitable - ClojureScript Completion Toolkit

CircleCI Clojars Project cljdoc badge downloads badge

suitable provides static and dynamic code completion for ClojureScript tools.

It provides two complementary completion sources:

  • It integrates with the CLJS analyzer and using the compilation state for "static" symbol completion. This functionality was briefly part of compliment, and before this - Orchard and cljs-tooling.
  • It can use a CLJS REPL session to query and inspect JavaScript runtime state, allowing code completion for JavaScript objects and interfaces.

Static code completion

The static code completion is based on analysis of the ClojureScript compiler state. This approach was pioneered by cljs-tooling and the completion logic was subsequently moved to orchard, compliment and finally here.

Why here? Because it's very convenient from the user perspective to have a single library providing both types of completion.

This type of completion provides a compliment custom source for ClojureScript, so it's easy to plug with the most popular completion framework out there.

(ns suitable.demo
  (:require
   [compliment.core :as complete]
   [suitable.compliment.sources.cljs :as suitable-sources]))

(def cljs-sources
  "A list of ClojureScript completion sources for compliment."
  [::suitable-sources/cljs-source])

;; you can obtain the ClojureScript environment in many different ways
;; we'll leave the details to you
(binding [suitable-sources/*compiler-env* cljs-env]
  (complete/completions prefix (merge completion-opts {:sources cljs-sources})))

Note that you'll need to establish a binding to suitable-sources/*compiler-env* for the completion to work.

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.

Beware 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" "target" "resources"]
 :aliases {:build-dev {:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}
           :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 -M:suitable and use TAB to complete.

leiningen

First make sure that the normal leiningen setup works.

Add [org.rksm/suitable "0.4.1"] 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 used by CIDER's code completion middleware (as of CIDER 0.22.0), so no extra installation steps are required.

CIDER will always use the static code completion provided by suitable, regardless of the ClojureScript runtime, but the dynamic completion is not currently available with shadow-cljs. (See this ticket for more details)

In case you run into any issues with suitable's dynamic completion in CIDER you can disable it like this:

(setq cider-enhanced-cljs-completion-p nil)

You'll still be using suitable this way, but only its static completion mechanism.

VS Code Calva

Everything in the section above applies when using Calva.

The calva.enableJSCompletions setting controls dynamic completion, and it is enabled by default.

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 "RELEASE"} cider/piggieback {:mvn/version "RELEASE"}}}' -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))

Note: Make sure to use the latest version of cider-nrepl and piggieback.

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.

More Repositories

1

cider

The Clojure Interactive Development Environment that Rocks for Emacs
Emacs Lisp
3,539
star
2

clojure-mode

Emacs support for the Clojure(Script) programming language
Emacs Lisp
909
star
3

clj-refactor.el

A CIDER extension that provides powerful commands for refactoring Clojure code.
Emacs Lisp
771
star
4

cider-nrepl

A collection of nREPL middleware to enhance Clojure editors with common functionality like definition lookup, code completion, etc.
Clojure
673
star
5

sayid

A debugger for Clojure
Clojure
406
star
6

orchard

A fertile ground for Clojure tooling
Clojure
326
star
7

refactor-nrepl

nREPL middleware to support refactorings in an editor agnostic way
Clojure
257
star
8

inf-clojure

Basic interaction with a Clojure subprocess
Emacs Lisp
249
star
9

squiggly-clojure

Flycheck checker for Clojure, using eastwood and core.typed.
Emacs Lisp
204
star
10

clomacs

Simplifies Emacs Lisp interaction with Clojure and vice versa.
Emacs Lisp
200
star
11

clojure-cheatsheet

[DEPRECATED] The Clojure Cheatsheet for Emacs
Emacs Lisp
193
star
12

clojure-ts-mode

The next generation Clojure major mode for Emacs, powered by TreeSitter
Emacs Lisp
129
star
13

ac-cider

[DEPRECATED] Emacs auto-complete backend for CIDER
Emacs Lisp
80
star
14

ac-nrepl

[DEPRECATED] Emacs auto-complete backend for nrepl completions
Emacs Lisp
74
star
15

helm-cider

Helm interface to CIDER
Emacs Lisp
66
star
16

parseclj

Clojure Parser for Emacs Lisp
Emacs Lisp
60
star
17

cljs-tooling

[DEPRECATED] Tooling support for ClojureScript
Clojure
60
star
18

parseedn

EDN parser for Emacs Lisp
Emacs Lisp
59
star
19

example-config

A sample Emacs config for Clojure development to ease your pain
Emacs Lisp
37
star
20

haystack

Let's make the most of Clojure's infamous stacktraces!
Clojure
34
star
21

enrich-classpath

Enriches Lein/deps.edn dependency trees with Java sources, JDK sources, javadocs, etc
Clojure
32
star
22

cider-decompile

An extension to CIDER which provides a decompilation command
Emacs Lisp
27
star
23

cider-hydra

Hydras for CIDER
Emacs Lisp
24
star
24

cider-eval-sexp-fu

eval-sexp-fu.el extensions for CIDER.
Emacs Lisp
12
star
25

hackingcider

HTML
10
star
26

clojuredocs-export-edn

Daily EDN exports of ClojureDocs's database.
Clojure
9
star
27

logjam

An interactive, nrepl-oriented logging backend
Clojure
1
star
28

docs.cider.mx

CIDER's documentation site
Handlebars
1
star