Tutkain

Tutkain is a Sublime Text package for interactive Clojure development, powered by nREPL.

Note: Tutkain is alpha-quality software. It has bugs. There will likely be breaking changes. It requires an alpha version of Sublime Text (build >= 4000). You must have a Sublime Text license to use the alpha version of Sublime Text. For more information, see Disclaimers.

A screenshot of Tutkain's interface.

Features

Getting Started

To start using Tutkain, you must:

  1. Install Tutkain via Package Control.
  2. Start an nREPL server (preferably nREPL 0.8.3 or newer).
  3. Connect to the nREPL server from Tutkain.

Installing Tutkain

  1. If you don't have Package Control installed, in Sublime Text, to install Package Control, open Tools » Command Palette, and choose Install Package Control.
  2. Open the Command Palette again and choose Package Control: Install Package.
  3. In the list that opens, choose Tutkain and press Enter.

Starting an nREPL server

Tutkain currently doesn't know how to start an nREPL server. It only knows how to connect to one. That means that you must start an nREPL server yourself. See Questions & answers for more information.

You can start an nREPL server using Clojure CLI tools, Leiningen, or any other tool that can start an nREPL server for you.

If you already know how to start an nREPL server using nREPL 0.8.3 or newer in your Clojure project, you can jump straight to connecting to an nREPL server.

Clojure CLI tools

To start an nREPL server using the Clojure CLI tools:

  1. Change to the root directory of your Clojure project:

    cd /my/clojure/project
    
  2. Run:

    clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version "RELEASE"}}}' -M -m nrepl.cmdline
    
  3. Wait until the nREPL server prints a message like this:

    nREPL server started on port 52609 on host localhost - nrepl://localhost:52609
    

To make it easier to start an nREPL server using Clojure CLI tools, you can add an alias like this your user profile in ~/.clojure/deps.edn:

{:aliases
  {:nrepl {:extra-deps {nrepl/nrepl {:mvn/version "0.8.3"}}
           :main-opts ["-m" "nrepl.cmdline"]}}}

Then, to start an nREPL server, run:

clojure -M:nrepl

For more information on aliases, see the Clojure Deps and CLI Rationale.

Leiningen

Some of Tutkain's features (for example, auto-completion and goto definition) require nREPL 0.8.3 or newer. If your version of Leiningen ships with an older version of nREPL, you can configure Leiningen to use nREPL 0.8.3 like this:

  1. Open ~/.lein/profiles.clj in Sublime Text.

  2. Add an entry for nREPL under the :dependencies key under the :user key:

    {:user {:dependencies [^:replace [nrepl "0.8.3"]]}}
    

Then, to start an nREPL server using Leiningen:

  1. Change to the root directory of your Clojure project:

    cd /my/clojure/project
    
  2. Run:

    lein repl :headless
    
  3. Wait until Leiningen prints a message like this:

    nREPL server started on port 52570 on host 127.0.0.1 - nrepl://127.0.0.1:52570
    

Connecting to an nREPL server

Once you've started an nREPL server, you can connect to it from Tutkain:

  1. In Sublime Text, open your Clojure project, either via File » Open or Project » Open Project.

  2. Choose Tools » Command Palette.

  3. Choose Tutkain: Connect.

  4. Enter the name of the host where your nREPL server is running (by default, localhost) and press Enter.

  5. Enter the port number of the nREPL server and press Enter.

    If you started your nREPL server in the root directory of your Clojure project, Tutkain auto-detects the port number for you.

  6. Wait for Tutkain to print Clojure and nREPL version information into your REPL window in Sublime Text. For example:

    Clojure 1.10.1
    nREPL 0.8.3
    

    You can then start using Tutkain to evaluate code. If you're already familiar with another Clojure editor, you might want to jump straight into configuring key bindings instead.

Evaluating code

Once you're connected to an nREPL server, you can start using Tutkain to evaluate code.

With Tutkain, you can move your cursor anywhere on top of a unit of Clojure code — a form — like that and evaluate it. You can use Tutkain to evaluate code at three different levels:

  1. Outermost form
  2. Innermost form
  3. Current view

Evaluating the outermost form

To evaluate the form that's the outermost from your cursor, use the Tutkain: Evaluate Outermost Form command. Here's how to try it out:

  1. In Sublime Text, open the Command Palette, and choose Tutkain: New Scratch View.
  2. Type (inc 41) into the scratch view. Keep your cursor anywhere on top of (inc 41).
  3. Open the Command Palette, and choose Tutkain: Evaluate Outermost Form.

Tutkain: Evaluate Outermost Form is the evaluation command you'll likely use the most. You'll probably want to set up a key binding for it after you've tried it out.

Here's what that should look like:

The REPL view shows both the input it received, user=> (inc 41), and the result, 42. The user bit is the namespace in whose context the code was evaluated.

Tutkain: Evaluate Outermost Form evaluates the form enclosed by the outermost set of brackets. There's one exception: if the outermost form is a (comment) form, Tutkain evaluates the second-to-outermost form instead. For example, given a view like this:

(comment
  (inc 41)
  )

If your cursor is anywhere on top of (inc 41), instead of (comment ,,,), Tutkain evaluates (inc 41). The reason for that is to support a common way of writing Clojure: writing forms inside a (comment) form and evaluating them. No one ever wants to evaluate the (comment) form itself.

Evaluating the innermost form

You can also evaluate the form that is closest to your cursor — the innermost form. For example, imagine you have a form like this:

(map inc (range 10))

Instead of evaluating the whole thing, you can also evaluate just the (range 10) bit. Here's how to try it out:

  1. In Sublime Text, open the Command Palette, and choose Tutkain: New Scratch View.

  2. Type (map inc (range 10)) into the scratch view.

  3. Move your cursor to the left of the parenthesis on the left of range.

  4. Open the Command Palette, and choose Tutkain: Evaluate Innermost Form.

    This result appears in your REPL view:

    user=> (range 10)
    (0 1 2 3 4 5 6 7 8 9)
    

Here's how Tutkain: Evaluate Innermost Form determines what to evaluate:

  • If your cursor is immediately to the left of an open bracket, it evaluates the form the bracket opens.
  • If your cursor is immediately to the right of a close bracket, it evaluates the form the bracket closes.
  • Otherwise, it evaluates the form your cursor is in.

Evaluating the view

Most of the time, when you're writing Clojure, you're working inside a namespace. Here's what that might look like:

(ns my.app)

(defn square
  [n]
  (* n n))

(defn sum-of-squares
  [x y]
  (+ (square x) (square y)))

(comment
  (sum-of-squares 2 4)
  )

Imagine you want to load the forms that define square and sum-of-squares into your REPL. You could evaluate each outermost form individually, but sometimes it's easier to just load the entire view instead. Here's how to try that out:

  1. In Sublime Text, open the Command Palette, and choose Tutkain: New Scratch View.

  2. Copy and paste the code above into the scratch view.

  3. Open the Command Palette, and choose Tutkain: Evaluate View.

    :tutkain/loaded appears in your REPL view.

  4. Move your cursor on top of (sum-of-squares 2 4).

  5. Run Tutkain: Evaluate Outermost Form.

    This result appears in your REPL view:

    my.app=> (sum-of-squares 2 4)
    20
    

Showing evaluation results inline

Tutkain can also show evaluation inline.

To show evaluation results inline, add a key binding for the tutkain_evaluate_form command and set the inline_result arg to true. For example:

{
    "keys": ["ctrl+c", "ctrl+a"],
    "command": "tutkain_evaluate_form",
    "args": {"scope": "outermost", "inline_result": true},
    "context": [{"key": "selector", "operator": "equal", "operand": "source.clojure"}]
},

Inline evaluation results currently have these limitations:

  • No syntax highlighting (the Sublime Text API doesn't allow it)
  • No multi-line evaluation results

Evaluating input

To have Tutkain ask you to input a Clojure form and evaluate it:

  1. In Sublime Text, open the Command Palette.

  2. Select Tutkain: Evaluate Input.

  3. In the prompt that appears, type in some Clojure code and press Enter.

    The evaluation result appears in the REPL window.

Tutkain keeps a history the forms you evaluate via Tutkain: Evaluate Input. To browse the history, run Tutkain: Evaluate Input and press the up or down arrow keys. The REPL history is transient: it is not saved into a file. Don't rely on it to save your history indefinitely.

In general, prefer using (comment) forms and evaluating the outermost or the innermost form over Tutkain: Evaluate Input. That way you can control whether to retain a permanent record of what you evaluate.

Running tests

Once you're connected to an nREPL server, you can run clojure.test tests directly from Tutkain. You can either run all tests in your current namespace or just the test that's under your cursor.

Running tests in the current namespace

  1. Open a Clojure namespace with clojure.test tests.
  2. Open the Command Palette, and choose Tutkain: Run Tests in Current Namespace.

Running the test under the cursor

  1. Open a Clojure namespace with clojure.test tests.
  2. Move your cursor anywhere on top of a deftest form.
  3. Open the Command Palette, and choose Tutkain: Run Test Under Cursor.

Understanding the test results

Once the test run is complete, Tutkain adds markers into your view that indicate whether the test or assertion passed, failed, or caused an exception.

If a test assertion passes, Tutkain shows a green dot in the gutter on the left.

If a test assertion fails, Tutkain shows a red dot in the gutter on the left. In addition, Tutkain shows a link that says "diff" in the right-hand side of the view. If you click the link, Tutkain shows you a diff between the expected and the actual result of the assertion.

If your test throws an error, Tutkain shows an orange dot in the gutter on the left. In addition, Tutkain shows an orange outline around the test where the exception occurred. Tutkain also shows a link that says "show" in the right-hand side of the view. If you click that link, Tutkain shows you information about the exception.

Editing code

Expanding the selection

If you're not already familiar with ParEdit, the most useful command to get you started with editing Clojure is Tutkain: Expand Selection. It expands your current selection to include the entire form that's closest to your cursor.

For example, given:

(dotimes [n 5] (println "Hello, world!"))

If your cursor is on the left of [ and you run Tutkain: Expand Selection, Tutkain selects [n 5]. If you run the command again, Tutkain expands the selection to cover the entire (dotimes ,,,) form.

The easiest way to understand how Tutkain: Expand Selection works is to try it out. Run Tutkain: New Scratch View and paste some Clojure code into it. Then move your cursor to different positions and run Tutkain: Expand Selection.

The basic rule is that the command first expands the selection to the nearest form. The next invocation expands to the innermost set of brackets, the next to next set of brackets, and so on until there are no more forms to expand to.

Managing parentheses

Tutkain comes with a ParEdit implementation. ParEdit is a tool that manages parentheses for you and adds commands for moving things in and out of brackets.

Documentation for the Tutkain ParEdit implementation is forthcoming.

For now, see Dan Midwood's Animated Guide to Paredit and the fantastic Calva Paredit documentation for the kinds of things you can do with ParEdit. If you're already a dab hand at ParEdit, run Tutkain: Edit Key Bindings to see the ParEdit commands Tutkain supports.

Tutkain does its best to support multiple cursors. That includes ParEdit. If you spot something that doesn't work, please file an issue.

Using auto-completion

Note: Auto-completion requires nREPL 0.8.0 or newer and Sublime Text build 4050 or newer. Auto-completion currently does not support ClojureScript.

When you're connected to an nREPL server and type something into a view that uses the Clojure syntax, Tutkain tries to guess what you want to write.

The completion list only suggests symbols your REPL knows about. Before you have evaluated anything, Tutkain typically only suggests symbols in the clojure.core namespace.

Once you start evaluating things, Tutkain starts suggesting those things as well. Given a function like this:

(defn square
  [n]
  (* n n))

Once you evaluate that function using one of Tutkain's commands, Tutkain starts suggesting that function, too, when you type sq.

Tutkain does no static analysis of your code. It is only aware of the current state of your REPL. If you restart your nREPL server, that state resets.

Looking up symbol information

Note: Symbol lookup requires nREPL 0.8.0 or newer and Sublime Text build 4050 or newer. Symbol lookup currently does not support ClojureScript.

When you're connected to an nREPL server and move your mouse cursor over a Clojure symbol, Tutkain shows you the definition of that symbol.

Tutkain only shows you the definition for symbols your REPL knows about. Before you have evaluated anything, Tutkain only shows definitions for symbols in the clojure.core namespace.

Tutkain also supports navigating to the definition of a symbol. That includes Clojure symbols defined in the clojure.core namespace and other symbols your project depends on.

To navigate to the definition of a symbol, hover over the symbol, and click the name of the symbol in the popup.

You can also assign a key binding for both symbol lookup and goto definition. Run Tutkain: Edit Key Bindings and look for tutkain_show_symbol_information and tutkain_goto_symbol_definition.

Configuring key bindings

Tutkain comes with very few key bindings enabled by default. That is because it would be impossible to avoid key binding conflicts.

Instead, Tutkain comes with a set of example key bindings. You can pick and choose the key bindings you want to enable and adjust them to your liking.

To see the example key bindings, run the Tutkain: Edit Key Bindings command. Tutkain will open its list of example key bindings in the left-hand column and your current key bindings in the right-hand column. To take an example key binding into use, copy the key binding from the column on the left into the column on the right.

A Sublime Text key binding looks like this:

{
    "keys": ["ctrl+c", "ctrl+e"],
    "command": "tutkain_evaluate_form",
    "args": {"scope": "innermost"},
    "context": [{"key": "selector", "operator": "equal", "operand": "source.clojure"}]
},

The context key specifies that you can only use the key binding in a view that uses the Clojure syntax.

To get started, I recommend configuring a key binding at least for these commands:

  • Tutkain: Connect
  • Tutkain: Disconnect
  • Tutkain: Expand Selection
  • Tutkain: Evaluate Outermost Form
  • Tutkain: Evaluate View
  • Tutkain: Clear Output View

After you're comfortable with using all of the above, you can configure key bindings for the rest of the commands (ParEdit commands in particular) as you go.

Indenting code

When you press Enter in a Clojure or ClojureScript view, Tutkain indents your Clojure according to the rules outlined by Nikita Prokopov in his article Better Clojure formatting.

You currently cannot configure how Tutkain indents your code. Ideally, we'd have something like gofmt and you'd never need to think about it.

Until then, if you want your code indented differently, I suggest using something like SublimeLinter-contrib-joker or configure a Git hook for the Clojure formatter of your choice to indent your code after editing it.

Working with multiple REPLs

You can have multiple REPL views for each Sublime Text window. For example, if you're working on a project that uses both Clojure and ClojureScript, you can have one REPL view for Clojure and another for ClojureScript.

When you evaluate code, Tutkain always sends your evaluation to the REPL view that has focus in your current Sublime Text window.

To use multiple REPL views:

  1. Connect to all the nREPL servers you want to connect to.
  2. Activate the REPL view for the nREPL connection you want to send your evaluations to.
  3. Evaluate something.

Contributing to Tutkain

Tutkain is a tool I've made for myself. In the exceedingly unlikely event that you actually want to use it, you will inevitably find it lacking in one respect or another. Once that happens, feel free to open an issue or a pull request on GitHub.

I make no promises to implement the feature or merge your pull request. One of my main goals with Tutkain was to make it lightweight (for some value of lightweight), and I'd like to keep it that way.

You are, of course, always free to fork Tutkain and implement the feature yourself, if all else fails.

Using complementary tools

To augment Tutkain, I highly recommend using the wonderful clj-kondo either via SublimeLSP or SublimeLinter-contrib-clj-kondo.

To use clj-kondo with SublimeLSP:

  1. Install SublimeLSP.
  2. Download the clj-kondo LSP server package JAR file.
  3. In Sublime Text, open Preferences » Package Settings » LSP.
  4. Under the clients key, add an entry like this for clj-kondo:
"clj-kondo-lsp": {
   // Replace $DATE with the actual date in the JAR file name
  "command": ["java", "-jar", "/path/to/clj-kondo-lsp-server-$DATE-standalone.jar"],
  "enabled": true,
  "languageId": "clojure",
  "scopes": ["source.clojure"]
},

Then, when you open a Clojure(Script) file, you should see clj-kondo lint errors in Sublime Text.

Questions & answers

Why?

I like Sublime Text. See also Rationale.

Nothing seems to work.

Tutkain only works with the Clojure syntax definition it ships with. To make sure you're using Tutkain's Clojure syntax definition:

  1. Under View » Syntax » Open all with current extension as... » Tutkain, select the syntax of the file you currently have open (Clojure, ClojureScript, or EDN).

If that doesn't fix the problem, please open an issue.

Why doesn't Tutkain support starting a REPL?

Integrating an editor with a Clojure build tool is hard. I don't think I really want to do it. Also, this way Tutkain is not tied to the build tool du jour. As long as it has an nREPL server to connect to, it's happy.

Besides, when you start the nREPL server outside the editor, you can restart the editor without killing your REPL. Sublime Text is very good for people like me who like restarting their text editor a lot.

If you really want to start an nREPL server from Sublime Text, you can use a Sublime Text build system like this:

"build_systems": [{
   "name": "tools.deps",
   "cmd": ["clojure", "-A:dev:test:nrepl"],
   "selector": "source.clojure",
   "working_dir": "$folder",
   "keyfiles": ["deps.edn"],
}]

Alternatively, use something like Terminus to run the tool of your choice in a terminal inside Sublime Text.

Rationale

Before Tutkain, I used the fantastic Cursive for Clojure and other JVM languages, and Sublime Text for everything else. Eventually, I figured it'd be nice to use Sublime Text for Clojure, too. I wanted a Clojure editor that's simple, stable, and fast. The "stable" part I'm still working on.

If you use and like Cursive, CIDER, or Calva, chances are that Tutkain is not for you. If you're used to Sublime Text and you're just getting started with Clojure, Tutkain might at least serve as a gateway drug to better editors. If you're looking for a fast Clojure editor that lets you switch between projects quickly, you might want to give Tutkain a go.

Disclaimers

If you decide to give Tutkain a try, be prepared for breaking changes. That's in addition to the usual cavalcade of bugs you'll see in alpha-quality software.

Furthermore, Tutkain requires Sublime Text 4, which is currently in alpha and requires a Sublime Text license. REPL-powered auto-completion and clojure.test integration require Sublime Text build 4050 or newer. REPL-powered auto-completion and symbol lookup also rely on experimental features introduced in nREPL 0.8. Ideally, you'll want nREPL 0.8.3 or newer.

Acknowledgements