**NEW Running the debugger is now optional - you can launch the REPL and access other features in non-debug mode - code runs twice as fast**
Clojure Code is a VS Code extension/debugger for developing Clojure. It provides an interactive REPL as well as language support and debugging.
The quickest way to get started is to clone the github demo project and follow the (brief) instructions in the README.md file. If you want to get started with your own project right away then read on.
lib
directory of your Java installation).From the command palette (cmd-shift-p
) select Install Extensions
and search for Clojure Code
in the Extensions viewlet.
Set up the paths as appropriate for your system by going to Code->Preferences->Settings.
The path to the lein binary
// Absolute path to the lein command
"clojure.leinPath": "/usr/local/bin/lein",
The path to the tools.jar file
// Absolute path to the tools.jar file (JAVA JDI jar file)
"clojure.toolsJar": "/Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home/lib/tools.jar"
After installing the extension in VS Code and setting your paths in preferences you need to add The nREPL debug middleware to your project. If you are using leiningen the best way to do this is through a custom profile. For a description of profiles see the leiningen profiles documentation.
You can do this by adding the following to the profiles in your project.clj file or to profiles.clj.
{:debug-repl {:resource-paths [#=(eval (System/getenv "PATH_TO_TOOLS_JAR"))]
:repl-options {:nrepl-middleware [debug-middleware.core/debug-middleware]}
:dependencies [[debug-middleware #=(eval (System/getenv "DEBUG_MIDDLEWARE_VERSION"))]]}}
IMPORTANT The best way to make sure you are using the proper middleware version to match your extension version is to use dynamic evaluation of the version in your dependency definition. Clojure Code exports an environment variable on launch (DEBUG_MIDDLEWARE_VERSION
) that can be used in your profiles.clj
or other means of declaring the dependency. The sample profiles.clj file above shows how to do this for leiningen projects. If you are starting the REPL yourself and attaching the debugger to it then you need to be sure you start the REPL with the correct version of the debug-middleware. See the Clojure Dependencies section below for the current version.
Clojure Code supports launching REPLs as well as attaching to running REPLs. This is controlled using launch configurations in a launch.json file. We will demonstrate launching a REPL first and then demonstrate connecting to an existing REPL later. If you are unfamiliar with VS Code debugging or launch.json, it might be helpful to read through the documentation.
You can get started by opening a Clojure project in VS Code and creating a launch.json file. Open the Debug viewlet by clicking on the debug icon , then click on the gear icon in the upper right corner and select 'Clojure Debug' from the menu.
A launch.json file will be created with a default launch configuration. You should edit this file for your own environment. VS Code provides Intellisense support when editing this file to help you make valid choices. Also, as of version 0.4.0, you can set defaults for several of the fields in the extension preferences. These defaults will be used for any of the fields you leave blank. The full details of the available settings are documented at the end of this readme file, but for now the only fields you need to change are the following:
commandLine
- the exact command plus arguments needed to launch your REPL. The default uses leiningen and sets the profile to debug-repl
(the one defined in the example above). You do not need to use leiningen, but you do need to make sure the REPL uses the debug middleware. If you do use leiningen you can use the $lein_path
template variable here and the full path to the lein
binary will be filled in from your prefrences setting.Also, if you do not start the REPL on the same port as in your extension preference settings then you need to specify the port in the launch configuration using the replPort
setting. This gives you maximum flexibility in choosing the way to launch your code. It just has to run in (or provide) nREPL running with the debug middleware.
replPort
- Set this if you are not launching on the default port as specified in your extension preference settings.On a mac an example launch configuration might look like this
{
"name": "Clojure-Debug Internal Console",
"type": "clojure",
"request": "launch",
"commandLine": [
"$lein_path",
"with-profile",
"+debug-repl",
"repl",
":headless",
":port",
"7777"
],
"replPort": 7777,
"debugReplPort": 7778,
"debugPort": 9999,
"sideChannelPort": 3030
}
The extension can launch the REPL in three different ways: in the internal debug console, in an internal command terminal, or in an external terminal. This is controlled by the console attribute. The default uses the internal debug console. Running in a terminal can be useful if, for example, you need to type input into your program or if your program expects to run in a terminal environment.
**NEW Running the debugger is now optional when starting the REPL.**
You can launch the REPL and access all the other features in non-debug mode. Code runs twice as fast when not in debug mode - really useful if you are going to run many tests. In order to run without the debuger just add "debug": false
to your launch.json
file.
The functionality of the extension is not available unless a REPL is running. You need to either launch a REPL or attach to one. We will cover launching a REPL first.
Once you have set up your profile (or otherwise enabled the nREPL middleware) and created a suitable launch.json file you can launch the REPL by going to the Debug viewlet, choosing the launch configuration you want to use, then clicking the debug icon .
This can take a while (minutes for large projects). You should see output from the REPL as is starts up in the debug console or in the terminal (for terminal launches). Eventually you should see a screen like the following (note color change in status bar at bottom). You should see the status message 'Attached to process' at the bottom and information in the debug console about the running REPLs and namespaces that were loaded.
The main elements to the interface are labeled in the next screenshot. These are the debug console, where output from the REPL is displayed, the debug console input box, used to execute code in the user
namespace and at breakpoints, the status area, which displays messages related to the current operation, and the call stack vew that displays the active threads as well as frames when a breakpoint is encountered.
Using a configuration like the following, it is possible to attach to a running REPL (notice request
is set to "attach".
{
"name": "Clojure-Attach Console",
"type": "clojure",
"request": "attach",
"replPort": 7777,
"debugReplPort": 7778,
"debugPort": 9999,
"cwd": "${workspaceRoot}",
"sideChannelPort": 3030
}
Note that the REPL must have been started listening on a JDI port. This is possible on mac/linux using something like this following:
env HOME=/Users/foo CLOJURE_DEBUG_JDWP_PORT=9999 JVM_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9999 /usr/local/bin/lein with-profile +debug-repl repl :start :port 7777
Once the REPL is started, you can connect using the same procedure as the one used for launching a REPL, namely, choosing your "attach" configuration and clicking the debug icon . The following example illustrates this process.
There are two different ways to execute code in the REPL. The first way is to select code in an editor and execute that using the Clojure: Evaluate selected text
command from the command palette (shift+cmd+p
), or by using the key binding shortcut (cmd+alt+e
on mac or ctrl+alt+e
on win/linux). Code evaluated in this way is evaluated in the namespace of the open file. Evaluating code from within an editor is facilitated by a helper (shift+ctrl+m
) that expands the current selection to the next outer form. Repeated invocation will continue to expand the selection.
The second way to evaluate code is by typing it into the debug console input box as shown below. Code evaluated from the debug input box is normally evaluated in the context of the user
namespace. The exception to this is when the program is stopped at a breakpoint, in which case the code is evaluated in the context of the current frame (having access to any defined vars.). All three cases are shown below:
Note: to debug code the REPL must be started in debug mode (the default). Currently Clojure Code supports two kinds of breakpoints, line breakpoints and exceptions breakpoints. Line breakpoints let you set set a breakpoint at a specific line in a given file. These are set by clicking in the left margin of an open editor.
Breakpoints can be set for exceptions by checking the 'Exceptions' box in the 'Breakpoints' view. As discussed in the "Known Limitations" section below, exception breakpoints apply to both checked and unchecked exceptions. In the status bar to the right of the exception class icon is an input box that can be used to set the type of exceptions on which to break. The default is Throwable
.
In general it is advisable to leave exception breakpoints off and only turn them on (with the exception class set appropriately) when an exception is encountered, so that the code can be run again and execution is stopped at the exception. Also, if an exception breakpoint is hit, be sure to disable the breakpoint before continuing or you are likely to retrigger it.
In this example, the exception occurs within the core Java code for Clojure. VS Code cannot display source it can't access, but we can select the invoking Clojure frame farther up the stack to see that code and inspect the vars.
If you have the source for the Java code in your source path then VS Code will display that as well. Java variables can be inspected and invoked from Clojure code in the debug console input just as with Clojure vars.
Clojure code contributes three commands to support running tests. These can can be accessed from the command palette (cmd+shift+p
(mac) / ctrl+shift+p
(win/linux)) and allow you to run all tests, run a single test (the one under the cursor), and run all tests in the currently opened namespace. In order to run all tests you need to tell the extension which directories contain tests that are safe to run in parallel and which ones must be run sequentially. This is done using the
parallelTestDirs
and sequentialTestDirs
settings in the launch.json file.
While the tests are running a progress bar is shown in the status bar. After the tests complete the PROBLEM
panel provides a list of tests that failed or ran into errors while running. Clicking on these will jump to the location with the test file. Hovering over the red squiggly lines under the failed/errored tests pops up a summary of the problem.
Command | Command Palette Entry | Description | Key bindings |
---|---|---|---|
clojure.eval | Clojure: Evaluate selected text | Evaluate the selected text in the file's namespace | cmd+alt+e (mac) / ctrl+alt+e (win/linux) |
clojure.expand_selection | Clojure: Expand Selection | Expand selection to containing brackets/parentheses | shift+ctrl+m |
clojure.fix-namespace-declaration | Clojure: Fix Namespace Declaration | Update the namespace declaration and fix requires/imports. | |
clojure.load-file | Clojure: Load File | Load the currently open Clojure source file. | |
clojure.refresh | Clojure: Refresh Code | Refresh changed code without restarting the REPL. | |
clojure.superRefresh | Clojure: Super Refresh Code | Refresh all code without restarting the REPL. | |
clojure.run-test | Clojure: Run Test Under Cursor | Run the test under the cursor, optionally refreshing code first. | |
clojure.run-test-file | Clojure: Run Tests in Current Namespace | Run the tests in the current namespace, optionally refreshing code first. | |
clojure.run-all-tests | Clojure: Run All Tests | Run all the tests in the project after refreshing the code. |
If the debugger seems to have stopped working (but language features like docstring lookup or jump to definition still work), you might need to restart the debugger. You can do this by clicking on the restart icon . This will not trigger the launch indicator or change the color of the status bar. You need to examine the REPL output to determine if things have restarted. If all else fails, you may need to stop the REPL by clicking the stop button and then restart.
The environment utilizes several Clojure libraries to enable various features.
clojure.refresh
.Property | Type | Description | Default Value |
---|---|---|---|
commandLine | array | Command to run to launch the debugged program. | ["lein", "repl", ":start", "5555"] |
console | enum: [ "internalConsole", "integratedTerminal", "externalTerminal" ] | Console to to use for launched programs. Defaults to internal debug console. | "internalConsole" |
cwd | string | Workspace relative or absolute path to the working directory of the program being debugged. | The current workspace |
debugPort | number | JDI port on which the debugger should connect to the process to be debugged. | 8030 |
debugReplPort | number | Port on which the client/debugger nREPL should listen. | 5556 |
debug | boolean | If true the REPL starts in debug mode | true |
env | map | Environment variables passed to the program. | {} |
leinPath | string | Path the the lein executable. | "/usr/local/bin/lein" |
refreshOnLaunch | boolean | Automatically load all namespaces on launch. | true |
replHost | string | Host on which the debugged REPL is running | "localhost" |
replPort | number | Port on which the debugged nREPL is listening. | 5555 |
sideChannelPort | number | Port on which the debugger should talk to the extension. | 3030 |
toolsJar | string | Path to the tools.jar in your Java installation. | "${env.JAVA_HOME}/lib/tools.jar" |
parallelTestDirs | array | Directories relative the current working directory with tests that should be run in parallel, i.e., are thread-safe | ["test"] |
sequentialTestDirs | array | Directories relative to the current working directory with tests that cannot be run in parallel with other tests, i.e., are not thread-safe | [] |
console
, comandLine
, and env
do not apply to attach
configurations.