Tutkain

Tutkain is a zero-dependency Sublime Text package for interactive Clojure development.

Note:

  • Tutkain is alpha-quality software. It has bugs. There might be breaking changes.
  • Tutkain requires Sublime Text 4 or newer.
  • Tutkain requires Clojure 1.10 or newer.
  • Tutkain requires Java 11 or newer.
  • 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 a Clojure socket REPL, a shadow-cljs socket REPL, or a Babashka socket REPL.
  3. Connect to the socket REPL from Tutkain.
  4. (Optional) Configure key bindings for the most commonly used commands.

Installing Tutkain

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

Starting a Clojure socket REPL

If you already know how to start a Clojure socket REPL, you can jump straight to connecting to a socket REPL.

Clojure CLI tools

To start a socket REPL using the Clojure CLI tools:

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

    cd /my/clojure/project
    
  2. Run:

    clj -J-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"
    

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

{:aliases
 {:socket-repl {:exec-fn clojure.core.server/start-server
                :exec-args {:name "server"
                            :port 5555
                            :accept clojure.core.server/repl
                            :server-daemon false}}}}

Then, to start a socket REPL, run:

clj -X:socket-repl

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

Leiningen

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

  2. Under the :user key, add a :socket-repl entry like this:

    {:user {:socket-repl {:jvm-opts ["-Dclojure.server.repl={:port 5555 :accept clojure.core.server/repl}"]}}}
    

Then, to start a socket REPL using Leiningen:

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

    cd /my/clojure/project
    
  2. Run:

    lein with-profiles +socket-repl repl :headless
    

Starting a shadow-cljs socket REPL

Note: Tutkain requires shadow-cljs v2.22.10 or newer.

To connect to a shadow-cljs socket REPL, you must start a shadow-cljs watch. You can start a shadow-cljs watch from the command line or from the REPL.

Command line

To start a shadow-cljs watch for build ID :app from the command line with npx:

  1. Start the shadow-cljs watch from the command line:

    npx shadow-cljs watch app
    

REPL

To start a shadow-cljs watch from the Clojure REPL:

  1. Start a Clojure REPL with shadow-cljs in the classpath.

  2. In the Clojure REPL, evaluate:

    (require '[shadow.cljs.devtools.server :as server])
    (require '[shadow.cljs.devtools.api :as shadow])
    
    (server/start!)
    (shadow/watch :app)
    

Starting a Babashka socket REPL

Note: Tutkain requires Babashka v1.1.171 or newer.

  1. On the command line, to start a Babashka socket REPL, run:

    bb socket-repl
    

    There's now a Babashka socket REPL listening on port 1666.

Connecting to a socket REPL

Once you've started a socket REPL (see above), you can connect to it from Tutkain:

  1. (Optional) 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. Choose the type of socket REPL you want to connect to (Clojure, ClojureScript, or Babashka).

  5. Enter the name of the host where your socket REPL server is running (by default, localhost) and press Enter.

  6. Enter the port number of the socket REPL server (for example, 5555) and press Enter.

  7. If connecting to a shadow-cljs socket REPL, choose the shadow-cljs build ID you want to work on.

  8. Wait for Tutkain to print your runtime version information into your REPL window in Sublime Text. For example:

    Clojure 1.10.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.

You almost certainly want to configure a key binding to connect to a runtime instead of going through the command palette every time. For example, here's a key binding to connect to a Clojure runtime:

{
    "keys": ["ctrl+c", "ctrl+x"],
    "command": "tutkain_connect",
    "args": {
        "dialect": "clj",
        "host": "localhost",
        "port": 5555,
        "output": "panel",
    }
},

When you invoke this key binding, Tutkain immediately connects to a Clojure runtime listening on port 5555.

Customizing your REPL

To do things like automatically require namespaces or set dynamic variables such as *print-length*, you can use the init argument to the tutkain_connect command.

With the init argument,you can pass the fully-qualified name of a function that accepts no arguments to have Tutkain call that function when it connects to a socket server.

For example, given this function in the classpath of your Clojure runtime:

(ns my.repl)

(defn init
 []
 (require '[my.awesome.ns :as awyiss])
 (set! *print-namespace-maps* false)
 (set! *print-length* 16)
 (set! *print-level* 8))

You can define a key binding that looks like this:

   {
     "keys": ["ctrl+c", "ctrl+x"],
     "command": "tutkain_connect",
     "args": {
         "init": "my.repl/init",
         // ...
     }
 },

Then, once you connect to a socket server using that key binding, Tutkain calls my.repl/init.

If you use tools.deps, to add a function into the classpath of your Clojure runtime, you can add an alias like this into your ~/.clojure/deps.edn:

:user {:extra-deps {my.repl/my.repl {:local/root "/Users/me/repl"}}}

Then, when you start the socket server for Tutkain to connect to, enable the :user tools.deps alias.

For more information, see the Deps and CLI Guide.

Evaluating code

Once you're connected to a socket REPL, you can start using Tutkain to evaluate code. You can evaluate code at a number of different scopes. Here's a list of the available scopes:

  • Adjacent form
  • Innermost S-expression
  • Outermost S-expression
  • Active view
  • Namespace declarations

If you have text selected and use any of the first three scopes, Tutkain evaluates the selection instead.

Note: The examples that follow use the Tutkain: Evaluate command via the Sublime Text command palette. If you choose to continue using Tutkain, you ought to configure a key binding for every evaluation scope you find useful.

Evaluating the adjacent form

  1. In a view that contains Clojure code, move your caret such that it immediately precedes or follows any form.

    Note that a form is not necessarily an S-expression. For example, 42 is a form, but not an S-expression. (inc 42) is both a form and an S-expression.

  2. Open the Command Palette and choose Tutkain: Evaluate.

  3. Choose Adjacent form and press Enter.

    Tutkain prints the value of the form adjacent to your caret into the REPL view.

Evaluating the innermost S-expression

  1. In a view that contains Clojure code, move your caret such that it immediately precedes or follows any S-expression.

    An S-expression is anything surrounded by parentheses, brackets, or braces.

  2. Open the Command Palette and choose Tutkain: Evaluate.

  3. Choose Innermost S-expression and press Enter.

    Tutkain prints the value of the S-expression that immediately precedes or follows your caret into the REPL view.

Here's how the Innermost S-expression scope determines what to evaluate:

  • If your caret is immediately to the left of an open bracket, it evaluates the S-expression the bracket opens.
  • If your caret is immediately to the right of a close bracket, it evaluates the S-expression the bracket closes.
  • Otherwise, it evaluates the S-expression your caret is inside.

Evaluating the outermost S-expression

  1. In a view that contains Clojure code, move your caret such that it is anywhere on top of an S-expression.

  2. Open the Command Palette and choose Tutkain: Evaluate.

  3. Choose Outermost S-expression and press Enter.

    Tutkain the value of the outermost S-expression into the REPL view.

The Outermost S-expression scope 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 caret 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 active view

Note: Tutkain does not currently support evaluating the active view for ClojureScript. As a workaround, you can select the entire view and use Tutkain: Evaluate » Outermost S-expression.

Evaluating the active view evaluates all code in the active view.

  1. In a view that contains Clojure code, open the Command Palette and choose Tutkain: Evaluate.

  2. Choose Active view and press Enter.

    Tutkain prints the value of the last Clojure expression in the active view into the REPL view.

Evaluating namespace declarations

You can use Tutkain to evaluate every namespace declaration in the active view.

  1. In a view that contains Clojure code, open the Command Palette and choose Tutkain: Evaluate.

  2. Choose Namespace declarations and press Enter.

    Tutkain evaluates every namespace declaration (ns form) in the active view.

Evaluating forms up to a point

Instead of evaluating the entire form, you can use Tutkain to evaluate a form up to the point where the caret currently is.

For example, given that the pipe character (|) represents the caret, and given:

(-> 1 inc| dec)

If you run tutkain_evaluate with up_to_point scope, Tutkain evaluates (-> 1 inc) instead of the entire form.

If your caret is not inside an S-expression, up_to_point evaluates every top-level form up to that point. For example, given:

(inc 1)
(inc 2)|
(inc 3)

If you run tutkain_evaluate with up_to_point scope, Tutkain evaluates (inc 1) and (inc 2). This can be useful when you want to evaluate every top-level form in the namespace up to a certain point.

To evaluate code up to a point:

  1. In a view that contains Clojure code, open the Command Palette and choose Tutkain: Evaluate.

  2. Choose Up to Point and press Enter.

Marking a form and evaluating it

To mark a form and (repeatedly) evaluate it later:

  1. Move your caret on top of a form.
  2. Run Tutkain: Mark Form.

Then, to evaluate the marked form:

  1. Run Tutkain: Evaluate > Mark.

Optionally, specify a key binding like this:

{
  "keys": ["ctrl+a", "ctrl+a"],
  "command": "tutkain_evaluate",
  "args": {"scope": "mark"},
  "context": [{"key": "selector", "operator": "equal", "operand": "source.clojure"}]
},

Evaluating input

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

  1. In Sublime Text, open the Command Palette.

  2. Choose Tutkain: Evaluate.

  3. Choose Input.

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

    Tutkain prints the evaluation result in the REPL view.

Tutkain keeps a history the forms you evaluate using the Input scope. To browse the history, run Tutkain: Evaluate, select Input, and press the up or down arrow keys. The REPL history is transient; don't rely on it to save your history indefinitely.

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

Evaluating snippets

You can assign a key binding that lets you evaluate snippet of code. That is, you can write a snippet and have Tutkain ask you to fill in the blanks. For example, you can assign a key binding that lets you def the symbol under the caret.

Here's a key binding that lets you do that:

{
  "keys": ["ctrl+p", "ctrl+e"],
  "command": "tutkain_evaluate",
  "args": {"scope": "input", "snippet": "(def $FORMS[0] ${1})$0"},
  "context": [{"key": "selector", "operator": "equal", "operand": "source.clojure"}],
},

When you invoke that key binding, this happens:

  1. Tutkain replaces $FORMS[0] with the value under the first caret.
  2. Tutkain prompts for input to replace ${1}.

When you press Enter, tutkain evaluates the snippet with all the blanks filled in.

Here's a video that demonstrates how this works:

Automatic namespace switching

When you evaluate Clojure code, you always do it in the context of a namespace. When you send a form for evaluation, before evaluating the form, Tutkain sets your current Clojure namespace to the namespace indicated by the first (usually only) ns form in that view.

To disable automatic namespace switching, choose Tutkain: Edit Settings and set auto_switch_namespace to false.

Interrupting evaluations

Note: Tutkain currently does not support interrupting evaluations for ClojureScript.

To interrupt an evaluation, use Tutkain: Interrupt Evaluation.

When you do that, Tutkain calls the .interrupt method on the REPL thread. That means you can only interrupt things that are explicitly interruptible.

In other words, Tutkain will not try to forcefully kill the REPL thread. For example, if you evaluate (range) without doing (set! *print-length* 1024) first, you'll have to restart your REPL.

Prompting the user for evaluations

The Tutkain: Prompt command opens an input panel at the bottom of the screen that prompts you for things to evaluate. It is intended to be used with nested clojure.main REPLs. For "regular" evaluation needs, you should continue to prefer comment forms etc.

Note: Tutkain: Prompt is currently not compatible with using a panel for REPL output. If you need to use Tutkain: Prompt, you might want to use a regular view for REPL output instead.

Choosing evaluation output

By default, Tutkain prints evaluation output into the REPL output panel or view. You can tell Tutkain to show evaluation result inline; to copy evaluation result into the clipboard; or to replace the current selection with the evaluation results.

Showing evaluation results inline

To show evaluation results inline, add a key binding for the tutkain_evaluate command and set the output arg to "inline". For example:

{
  "keys": ["..."],
  "command": "tutkain_evaluate",
  // Evaluate the innermost form, show evaluation results inline
  "args": {"scope": "innermost", "output": "inline"},
  // Only enable this key binding in a Clojure context
  "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

Copying evaluation results to clipboard

To have Tutkain copy evaluation results into the clipboard, define a key binding like this:

{
  "keys": ["..."],
  "command": "tutkain_evaluate",
  "args": {
    // ...
    "output": "clipboard"
  },
  // ...
},

Replacing the current selection

To have Tutkain replace the current selection with the evaluation result, define a key binding like this:

{
  "keys": ["..."],
  "command": "tutkain_evaluate",
  "args": {
    // ...
    "output": "selection"
  },
  // ...
},

The selection output is useful when you want to replace (random-uuid) with its result, for example.

Dealing with exceptions

When you evaluate something that throws an exception, Tutkain only shows you the exception message.

To see the exception stack trace, evaluate *e. You'll be using it often, so it's useful to assign a key binding to evaluate *e. For example:

{
    "keys": ["ctrl+c", "ctrl++"],
    "command": "tutkain_evaluate",
    "args": {"code": "((requiring-resolve 'clojure.repl/pst) *e)"}
}

Exploring exception stack traces

When your evaluation throws an exception, you can use Tutkain: Explore Stack Trace to explore the stack trace for that exception (the current value of *e).

When exploring the stack trace, Tutkain will also navigate to Java sources if it finds them. Tutkain looks for Java base module sources in $JAVA_HOME/src.zip and $JAVA_HOME/lib/src.zip. Alternatively, you can set the tutkain.java.src.zip system property to point to the Java base module source ZIP file.

For Java dependencies, Tutkain will look for a file with the suffix *-sources.jar next to the JAR file that contains the Java classes. If you have Maven installed, to download sources for all of the Java dependencies in your project, in the directory where your deps.edn or project.clj file is, run:

# with Clojure CLI
clj -A:my:aliases -Spom

# or with Leiningen
lein pom

# Then
mvn dependency:sources

You will have to redownload sources (run the mvn command) again whenever you update your project dependencies.

Running tests

Once you're connected to a socket REPL, 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 caret.

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.
  3. Choose Namespace.

Running the test under the caret

  1. Open a Clojure namespace with clojure.test tests.
  2. Move your caret anywhere on top of a deftest form.
  3. Open the Command Palette, and choose Tutkain: Run Tests.
  4. Choose Var.

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.

Showing unsuccessful tests

After running tests, to show a list of all of the unsuccessful tests (tests that failed or threw an exception), use the Tutkain: Show Unsuccessful Tests command.

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 caret.

For example, given:

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

If your caret 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 caret 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.

As of v0.12.0 of Tutkain, you must explicitly enable all ParEdit key bindings. At the very least, you probably want Tutkain to help keep your parentheses balanced. To do that, enable this set of key bindings.

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 carets. That includes ParEdit. If you spot something that doesn't work, please file an issue.

Using auto-completion

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

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 and possible other namespaces that Clojure has loaded when starting the REPL process.

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 runtime, that state resets.

Looking up symbol information

When you're connected to a runtime, move your caret onto a Clojure symbol or a keyword that names a Clojure spec, and use the Tutkain: Show Information command, Tutkain shows you the definition of that symbol or clojure.spec-naming keyword.

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 and possible other namespaces that Clojure has loaded when starting the REPL process.

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, click the name of the symbol in the information popup.

You can (and probably should) assign a key binding for both information lookup and goto definition. Run Tutkain: Edit Key Bindings and look for tutkain_show_information and tutkain_goto_definition.

Configuring key bindings

Tutkain comes with no key bindings enabled by default. This is to avoid key binding conflicts and to avoid interfering with other packages that offer the same functionality as Tutkain.

Important: Prior to v0.12.0 (alpha), Tutkain had a small number of key bindings enabled by default. v0.12.0 of Tutkain removes all of those default key bindings. For instructions on how to restore those key bindings, see the Package Control installation message for v0.12.0.

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. Alternatively, to take the entire set of example key bindings into use, copy-paste everything from the left-hand column into the right-hand column.

A Sublime Text key binding looks like this:

{
    "keys": ["ctrl+c", "ctrl+c"],
    "command": "tutkain_evaluate",
    "args": {"scope": "outermost", "ignore": ["comment"]},
    "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.

The example key binding file Tutkain comes with has a section with commands you likely want to add key bindings for.

Feel free to change the key bindings themselves to whatever you feel comfortable with, of course.

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

To have Tutkain indent your Clojure according to the rules outlined by Nikita Prokopov in his article Better Clojure formatting when you press Enter in a Clojure or ClojureScript view, add a key binding like this:

{
  "keys": ["enter"],
  "command": "tutkain_insert_newline",
  "context": [
    {
      "key": "selector",
      "operator": "equal",
      "operand": "source.edn | source.clojure"
    },
    {
      "key": "auto_complete_visible",
      "operator": "equal",
      "operand": false
    },
    {
      "key": "panel_has_focus",
      "operator": "equal",
      "operand": false
    },
  ]
},

To configure Tutkain to indent your code for you when you press Tab, add a key binding like this:

 {
     "keys": ["tab"],
     "command": "tutkain_indent_sexp",
     "args": {"scope": "outermost"},
     "context": [
         {"key": "selector", "operator": "equal", "operand": "source.edn | source.clojure"},
         {"key": "auto_complete_visible", "operand": false},
         {"key": "has_next_field", "operand": false},
     ]
 },

If you want Tutkain to only indent the innermost S-expression in relation to your caret, use "scope": "innermost" instead.

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.

Using tap> for debugging

When debugging Clojure code, you'll often want to know the value of a local or a global definition in certain context. A common approach is to use prn (or equivalent) to print the value.

With Tutkain, you can also use tap> to pretty-print a value. To do that, you must first set Tutkain's tap_panel setting to true. Once that's done, you can begin using tap> for debugging.

For example, given a function like this:

(defn f
  [x]
  (tap> x)
  ,,,)

Now, whenever f is called, Tutkain will pretty-print the value of x in either a separate panel dedicated to tapped values or, if you're using a panel for REPL output, directly in your output panel.

Choosing connection mode

Note: Connection modes require Tutkain v0.18.0 or newer. Connection modes only pertain to the Clojure and Babashka runtimes. ClojureScript connections always use the RPC mode.

By default, Tutkain operates over two connections: one for the REPL, and one for everything else. This lets you use things like nested REPLs with Tutkain.

However, if you need Tutkain to operate over a single connection (e.g. if you're connecting over an SSH tunnel), you can tell Tutkain to use a Remote Procedure Call (RPC) style to communicate with the runtime using a single connection.

To use the RPC mode, either:

  1. Open the Command Palette.
  2. Choose Tutkain: Edit Settings.
  3. Set default_connection_mode to "rpc".

Or add "mode": "rpc" into your key binding. For example:

 {
     "keys": ["ctrl+c", "ctrl+x"],
     "command": "tutkain_connect",
     "args": {"mode": "rpc"}
 },

Both the RPC and REPL connection mode have limitations.

Limitations of the RPC mode

  • You cannot use nested REPLs.
  • Things like (read) and (read-line) are not supported.
  • Inferior support for very large evaluation results (not by definition, however — could be improved).

Limitations of the REPL mode

  • Requires the client to establish two connections to the runtime.
  • Inferior support for switching namespaces while long-running evaluations are in progress.
  • Subjectively inferior support for evaluating incomplete forms (where the RPC mode might yield a syntax error, the REPL mode might expect for additional input)

Choosing REPL output user interface

By default, Tutkain prints evaluation results, standard input, and standard output into a Sublime Text output panel. You can also tell Tutkain to print them into a regular Sublime Text view instead. To do that, define a key binding like this:

 {
     "keys": ["ctrl+c", "ctrl+x"],
     "command": "tutkain_connect",
     "args": {"host": "localhost", "output": "view"}
 },

The panel setting is useful on smaller displays (like a laptop screen, for example), if you prefer using inline evaluation results, or if you prefer to use a separate tool like Tab, Reveal, Portal to display REPL output.

The view setting is useful if you want to use a vertical layout, or if you never want to automatically hide the REPL output.

Here's a list of differences between using a view and a panel for REPL output:

  1. You can hide the panel by pressing the Escape key. The panel also hides automatically if you enable another panel. You can define key bindings to show and hide the output panel.
  2. When using a panel, if you have the tap_panel setting enabled, Tutkain prints both REPL output and any values you tap> in the same panel.
  3. When using a panel, if you connect to both Clojure and ClojureScript runtimes, Tutkain prints the REPL output for both runtimes in the same panel.

Working with Clojure Common files

When working with a Clojure Common (.cljc) file, you can choose whether to use the Clojure or ClojureScript REPL for evaluating code.

By default, Tutkain evaluates Clojure Common files as Clojure. You can use Tutkain: Choose Evaluation Dialect to switch between Clojure and ClojureScript evaluation.

Working with multiple runtimes

You can have multiple REPL views for each Sublime Text window.

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 runtimes you want to connect to (or open multiple connections to the same runtime).
  2. Activate the REPL view for the runtime you want to send your evaluations to.
  3. Evaluate something.

Working with dependencies

Dynamically adding a new dependency

Note: This feature requires Clojure 1.12.0-alpha2 or newer.

To add a new dependency into the runtime without having to restart it:

  1. Open the Command Palette.
  2. Choose Tutkain: Add Lib.
  3. Choose the repository to search (Clojars or Maven Central).
  4. Type a search query and press Enter.
  5. In the search results, choose the lib you want to add and press Enter.

You can now use the dependency you added.

Synchronizing dependencies

Note: This feature requires Clojure 1.12.0-alpha2 or newer.

To use a dependency you've added into deps.edn without having to restart the runtime:

  1. Open the Command Palette.
  2. Choose Tutkain: Synchronize Dependencies.

You can now use the dependency you added into deps.edn.

Using nested clojure.main REPLs

To start a nested REPL via clojure.main/repl and retain syntax highlighting in the REPL view, pass tutkain.repl/*print* as the :print option. For example:

(clojure.main/repl :print tutkain.repl/*print*)

Querying the Clojure runtime

Searching vars by name and documentation

Note: This feature is currently not supported for ClojureScript.

To search the current runtime for vars, functions, and macros whose name or docstring match a given pattern:

  1. Open the Command Palette.
  2. Choose Tutkain: Apropos.
  3. Type a regular expression to search for.
  4. Press Enter

Tutkain will show you a quick panel with items that match the given regular expression. You can type into the quick panel to further filter the results.

Listing all public vars in a namespace

Note: This feature is currently not supported for ClojureScript.

  1. Open the Command Palette.
  2. Choose Tutkain: Dir.

If your caret is on top of a symbol naming a var (e.g. clojure.set/project), Tutkain lists all public vars in the namespace of that symbol (e.g. clojure.set).

If your caret is not on top of a qualified symbol, Tutkain prompts you for the namespace whose public vars to list.

Tutkain will then show you a quick panel with information about the public vars in that namespace. You can type into the quick panel to further filter the results.

Listing all loaded libs

To list all libs that have been loaded into the runtime:

  1. Open the Command Palette.
  2. Choose Tutkain: Loaded Libs.

Tutkain will show you a quick panel with a list of all the libs loaded into the current runtime. You can type into the quick panel to further filter the results.

Highlighting and renaming locals

Note: This feature is experimental and subject to change or removal.

Starting from Tutkain v0.9.0 (alpha), if you have clojure.tools.analyzer.jvm (or clojure.tools.analyzer for ClojureScript) in your classpath, Tutkain will highlight local usages whenever you move your caret on top of a local. For example:

If you move your caret on top of x, Tutkain highlights all usages of that local. You can also define a key binding to select all usages of a local. For example:

{
    "keys": ["ctrl+x", "ctrl+r"],
    "command": "tutkain_select_locals"
},

Once selected, to rename the local, type the new name.

Removing namespaces, namespace mappings, and namespace aliases

To remove a namespace mapping (ns-unmap):

  1. Open the Command Palette.
  2. Choose Tutkain: Remove Namespace Mapping.

To remove a namespace alias (ns-unalias):

  1. Open the Command Palette.
  2. Choose Tutkain: Remove Namespace Alias.

To remove a namespace:

  1. Open the Command Palette.
  2. Choose Tutkain: Remove Namespace.

Editing settings

To edit settings:

  1. Open the Command Palette.
  2. Choose Tutkain: Edit Settings.

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

I recommend the wonderful Pep or SublimeLSP and clojure-lsp for static analysis.

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 a socket REPL server to connect to, it's happy.

Besides, when you start the socket REPL 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 a socket REPL from Sublime Text, you can use a Sublime Text build system like this:

"build_systems": [{
   "name": "tools.deps",
   "cmd": ["clojure", "-X:socket-repl"],
   "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.

I get ERR: Not connected to a Clojure REPL when evaluating from a ClojureScript file, even though I'm connected to a ClojureScript REPL.

Make sure the syntax of your current view is set to ClojureScript (Tutkain).

Rationale

Tutkain has these aims:

  • Fast
  • Simple
  • Stable (once out of alpha)
  • Dependency-free
  • REPL first
  • Seamless support for switching between Clojure and ClojureScript

When evaluating code, Tutkain operates on a plain character stream. That means that if you want to do things like start nested REPLs, Tutkain won't get in you way. Tutkain uses a separate channel for chatter related to editor tooling (auto-completion, interrupts etc.)

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.

Getting help

Either open an issue or ask for help in the #tutkain channel over at the Clojurians Slack server.

Acknowledgements

Alternatives

For programming Clojure using Sublime Text, Clojure Sublimed is a great alternative.