Dialects and runtimes

Jonquil evaluates through a dialect profile: what a language's files look like, how they're painted, and which evaluator runs them. Six profiles exist today: Jolt, ClojureDart (experimental), JVM Clojure, ClojureScript (experimental), jank (experimental) and nREPL, which attaches to any nREPL server you started.

The status bar's dialect label shows which one a window uses and whether its runtime is up:

DialectLabel
JoltJolt, or Jolt (nREPL) when it runs out of process
ClojureDartClojureDart (attached) / ClojureDart (not running)
JVM ClojureClojure (JVM) (attached) / Clojure (JVM) (not running)
ClojureScript, shadow-cljsClojureScript (shadow-cljs) (attached) / (not running)
ClojureScript, own runtimeClojureScript (node) (running) / (not running), or ClojureScript (browser) with the same endings
jankjank (running) / jank (not running) (until the first evaluation starts one)
nREPLnREPL (attached) / nREPL (not running)

Choosing a dialect

Jonquil picks a dialect and a runtime for each window, in this order:

  1. .jonquil.edn in the project, if it names one.
  2. A single profile marker in the project (Jolt's is deps.edn).
  3. The open file's extension.
  4. The :dialect saved in settings.edn.
  5. Jolt, when none of the above decides.

The project is the nearest directory, at or above the open file, holding .jonquil.edn or a profile marker. A loose file or an unsaved window has no project, so only the extension and the settings default are left.

How each dialect gets picked

  • ClojureDart: marker pubspec.yaml; .cljd is ClojureDart even when a deps.edn is also present, while .clj, .cljc and anything else stay Jolt's.
  • JVM Clojure: only {:dialect :clj} picks it inside a project. A settings default of JVM Clojure applies to loose or unsaved files only.
  • ClojureScript: marker shadow-cljs.edn attaches to your shadow-cljs server; a .cljs file elsewhere runs on node. {:runtime :launch} or {:runtime :attach} forces the runtime (see ClojureScript).
  • jank: .jank files, or {:dialect :jank}.
  • nREPL: only {:dialect :nrepl}. A bb.edn or Basilisp project stays Jolt's until you say so (see Any nREPL server).

.jonquil.edn

A .jonquil.edn at the project root pins the dialect and, for Jolt, chooses the runtime:

{:dialect :jolt :runtime :in-process}

Both keys default independently, so either can be left out. A file that isn't valid EDN, or isn't a map, never blocks anything: the console prints a line like ; project: .jonquil.edn isn't valid EDN, so it is ignored and selection falls through to the next rule.

  • {:dialect :jank} pins jank (see jank).
  • {:dialect :nrepl} attaches to any nREPL server. :nrepl says where: {:port 1667}, {:host "box" :port 1667} or {:port-file "path/to/port"} (see Any nREPL server).
  • {:dialect :clj} is the only way to reach JVM Clojure: the project attaches to its own nREPL server instead of running under Jolt (see JVM Clojure).
  • :auto-require lists namespaces to load as Jonquil connects. It works for JVM Clojure and Jolt; other dialects ignore it and the console says so (see Auto-require).
  • :parinfer sets Parinfer for the project's tabs, overriding the global setting either way: :smart, :indent or :paren turns it on here, and :off keeps it off here even when the setting is on.
  • :format-style pins what Format All and Format Selection produce for the project's tabs, so the repo formats one way for everyone whatever each person's Parinfer mode: :paren-stable always runs the Paren pass over the formatter's output, :plain never does. Left out, a format follows the tab's mode (see Parinfer).
  • :remember-session? decides whether the project's evaluations are recorded for last session's replay, overriding the setting in settings.edn either way: false keeps this project off disk, and true records it even when the setting is off.

Running Jolt out of process

In a project with a deps.edn, Jolt runs out of process. Jonquil starts a separate Jolt nREPL server in the project directory and talks to it over a socket. A program that calls System/exit or shutdown-agents, redefines something in clojure.core, or otherwise takes Jolt down takes down that child, not the window. The project's deps.edn is the child's classpath.

{:runtime :in-process} in .jonquil.edn keeps a project inside Jonquil's process instead:

  • Its deps.edn is resolved as the jolt command does, so files can require the project's other files and dependencies, and F12 opens either.
  • A git or Maven dependency is fetched on first need ("Resolving deps.edn..."; Stop cancels). On failure the Run goes ahead on :paths alone and the console says why.
  • Edits to the project's deps.edn re-resolve; edits to a :local/root dependency's own do not (touch the project's file).

{:runtime :nrepl} asks for a child in a folder without a deps.edn. A file outside any project always runs in process. An untitled tab follows the window's folder: out of process in a deps.edn window, in process in a window with no folder.

Start lines, Stop, switching projects, and stdin

  • The console reports the child's pid, port and project directory at start.
  • Stop interrupts first; if the child doesn't answer in about a second, it kills and restarts the child and says so.
  • Opening a file from a different project switches the session; the old evaluator is stopped and the console says so.
  • stdin, the inspector and jump to definition work through Jonquil's own middleware, written to ~/.cache/jonquil/nrepl-middleware (or under $XDG_CACHE_HOME). If that can't be written or loaded, Jolt starts without them and the start lines say why; {:runtime :in-process} is the way back. A plain nREPL server gets no stdin (read-line returns nil).
  • Output streams as it prints with jolt-lang/nrepl v0.4.0 or later, and from v0.4.2 a Run of a saved file records that file as its vars' source, for jump to definition.

Where the nREPL middleware comes from

Interrupt needs Jonquil to inject jolt-lang/nrepl's middleware. It uses, in order:

  1. A checkout at $JONQUIL_JOLT_NREPL or ~/dev/jolt-lang/nrepl (keep it at v0.4.2 or later).
  2. jolt-lang/nrepl v0.4.2 as a git dependency. The first start clones it into ~/.gitlibs (about 3 seconds; needs git and a network).
  3. If the fetch fails, a plain nREPL server, and the console says so. It has no interrupt, so Stop kills and restarts the child, and read-line has no stdin. The next start retries the fetch.

.nrepl-port

Jonquil's Jolt child writes no .nrepl-port, and never touches one your project has, so another editor or a JVM Clojure window can't mistake the child for your server. Your own jolt nrepl-server writes the file as usual. The child uses Jonquil's middleware list, so a :nrepl/middleware in your deps.edn isn't loaded there.

This page is docs/guide/dialects/index.md in the Jonquil repository.

Enter to open · Esc to close