1. Overview

vim-iced logo

vim-iced is a Vim8 / Neovim plugin to provide a Clojure interactive development environment.

This project is heavily inspired by CIDER, and the goal of this project is to provide a Clojure development environment to Vim that is comparable(or more) to CIDER.

The assumption of "comparable to CIDER" varies from person to person. Although it is subjective, I assume "comparable to CIDER" is that there is no obstacle to development at work.

2. Getting Started

2.1. Installation

vim-iced itself and vim-sexp are the minimal requirements.

In addition, if there is one of follows, you can use interactive UI for selecting one from multiple candidates (e.g. selecting a ns which you’d like to add)

If anything is okay, ctrlp.vim is easy to install and stable.

2.1.1. Installing plugins

At first, if you don’t have a plugin manager, please install vim-plug.

$ curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
$ curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \

Next, add a vim-plug section to your ~/.vimrc (or ~/.config/nvim/init.vim for Neovim)

" Specify a directory for plugins
call plug#begin('~/.vim/plugged')

" One of following
Plug 'ctrlpvim/ctrlp.vim'
Plug 'junegunn/fzf'
Plug 'liuchengxu/vim-clap'

" Requires
Plug 'guns/vim-sexp',    {'for': 'clojure'}
Plug 'liquidz/vim-iced', {'for': 'clojure'}

call plug#end()

" Enable vim-iced's default key mapping
" This is recommended for newbies
let g:iced_enable_default_key_mappings = v:true

Then start Vim/Neovim, and execute the following command to install dependent plugins.


2.1.2. iced command

iced is a a utility command to make you easy to use vim-iced, and it comes with vim-iced. Setup is easy, just add the bin directory under the installed vim-iced directory to $PATH environmental variable. With above vim-plug setting, ~/.vim/plugged/vim-iced/bin is the directory to add to $PATH.

# For bash or zsh
$ export PATH=$PATH:~/.vim/plugged/vim-iced/bin

# For fish
$ set -x PATH ~/.vim/plugged/vim-iced/bin $PATH

To confirm setup, run the following command.

$ iced version

See iced command for more information.

2.2. Quick start

Let’s create a sample project for quick start. Here Leiningen is used to create a project template.

$ lein new hello-iced
$ cd hello-iced

We use iced command to launch REPL instead of lein command. This allows iced command to take care of all the additional libraries and REPL settings you need.

$ iced repl

Open vim and execute IcedConnect command.

$ vim src/hello_iced/core.clj

You succeeded to connect REPL if you see "Connected" message, so let’s execute the next command.

:IcedEval (+ 1 2 3 4 5)

Do you see 15 as a result? If so, your vim has started working with Clojure’s REPL finally!

3. Evaluation

The evaluation of S-expression is the most important element in REPL driven development.

3.1. Ranges

There are 3 main ranges to evaluation in vim-iced.

  • inner element

  • outer list

  • outer top list

See the following figure for the concrete ranges.

Evaluation range

If you enable default key mappings, following key mappings are available.

Table 1. Default key mappings

inner element


outer list


outer top list


See help file for other default key mappings.

3.2. Results

The evaluation result is echoed on command-line and displayed in a popup at the end of line. However, the displayed result is only the returned value, so for example, the contents output by println are not displayed.

The contents output to standard output are displayed on the Stdout buffer.

The highlight setting for popup can be modified by g:iced#eval#popup_highlight.

3.3. Marks

vim-iced also provides ways to evaluate forms by Vim’s mark. This feature is implemented by Conjure originally.

Command Default key mapping Description



Evaluate the outer list (not outer top list) on the specified mark.

Type a key to specify the mark after executing this command.



Re-evaluate the outer top list which is evaluated last.
Last evaluated list is marked to g:iced#eval#mark_at_last.

For example, when you have a mark m on any position, <Leader>eam will evaluate the outer list on the mark m from any position.

3.4. Yank

The evaluated result is yanked to numbered registers. Like vim’s behavior, iced shifts the previous result of register 1 into register 2, 2 into 3, and so forth.

If you have values you don’t want to store, See g:iced#eval#values_to_skip_storing_register.

When you specify a register like yank, <Plug>(iced_eval) operator will yank the result.

E.g. "x<Leader>ee will yank a result to register x.

Yanking to " register is skipped for now.

If you’d like to define a custom mapping, try below.

" mapping for "xee
nmap <silent> ee <Plug>(iced_eval)<Plug>(sexp_outer_list)``

3.5. Context

There are times when you want to evaluate the contents of a function for confirmation.

(defn greet [s]
  ;; We'd like to evaluate the following code!
  (str "hello " s))

It is tired to bind arguments separately, or to write function calls in the comment form every time.

(comment (def s "iced"))
;; or
(comment (greet "iced"))

This is where evaluating codes in context help you. There is a default key bindings to evaluate code in a context.

Table 2. Default key mappings

outer list


With this mapping, the context input will be required.

The context is clojure.core/let style. For example, the context s "iced" will be expanded to the following code.

(clojure.core/let [s "iced"]
  (str "hello " s))

See <Plug>(iced_eval_in_context) for more information.

Unresolved directive in evaluation.adoc - include::evaluation/spinner.adoc[]

4. Completion

4.1. Omni completion

vim-iced provides only omni completion. It is set to omnifunc for clojure filetype automatically by default.

Vim has a <C-x><C-o> key mapping for omni completion.

Example (📍 means cursor position)
(prin📍)    ;; Type `<C-x><C-o>` to show omni completion

4.2. Auto completion

vim-iced itself does not provide any auto completion methods.

However, the following completion plugins are supported as external plugins.

4.2.1. asyncomplete.vim

asyncomplete.vim is a async completion plugin in pure vim script for Vim8 and Neovim, and vim-iced-asyncomplete is a vim-iced plugin to work with this plugin.

To use vim-iced-asyncomplete, add followings to vim-plug section in your ~/.vimrc (or ~/.config/nvim/init.vim for Neovim)

Plug 'prabirshrestha/asyncomplete.vim'
Plug 'liquidz/vim-iced', {'for': 'clojure'}
Plug 'liquidz/vim-iced-asyncomplete', {'for': 'clojure'}

4.2.2. coc.nvim

coc.nvim is a intellisense engine for Vim8 and Neovim, and vim-iced-coc-source is a vim-iced plugin to work this plugin.

To use vim-iced-coc-source, do following installation steps.

  • Install coc.nvim (here is the installation step)

  • Add followings to vim-plug section in your ~/.vimrc (or ~/.config/nvim/init.vim for Neovim)

Plug 'liquidz/vim-iced', {'for': 'clojure'}
Plug 'liquidz/vim-iced-coc-source', {'for': 'clojure'}
  • Add the following definition to your coc-settings.json

    "coc.source.iced.enable": true

4.2.3. cmp-iced

nvim-cmp is a completion plugin for neovim coded in Lua, and cmp-iced is a vim-iced plugin to work with this plugin.

5. Reference

5.1. Docstring

There are two ways (explicit and implicit) to refer to a var’s docstring.

The explicit way is to execute one of the following commands.

Command Default key mapping Description



Shows the docstring for the var under the cursor in a popup window.


Opens a Document buffer, and shows the docstring in it.

The implicit way is the Auto Document feature. This feature will automatically display a one-line documentation string on the right of cursor line. This will be done by CursorMoved / CursorMovedI auto command and timer.

5.2. ClojureDocs

ClojureDocs is a community-powered documentation and examples repository for the Clojure programming language. Vim-iced can display ClojureDocs reference information for a provided search term or the symbol under the cursor. It opens a temporary window split which can be closed with IcedDocumentClose.

Command Default key mapping Description



Open a window, and show documents for the symbol under the cursor.


IcedClojureDocsOpen will create a cache file on your local machine.
If you get an out of date result, you can refresh the cache file with this command.

5.2.1. ClojureDocs on ClojureScript

ClojureDocs does not have docs for ClojureScript currently.

But if you would like to use Clojure documentation instead of ClojureScript, you can use it by enabling g:iced#clojuredocs#use_clj_docs_on_cljs.

5.3. Source code

IcedSourceShow or IcedSourcePopupShow is useful when you would like to refer to the source code itself. The keyboard shortcuts will show the source code for the symbol under the cursor, the commands can be used to search for the source code of any symbol that’s provided.

Command Default key mapping Description



Shows the source code in a popup window.



Opens a separate buffer(Document buffer) and shows the source code in it.

5.3.1. Enhanced source extraction

If g:iced_enable_enhanced_definition_extraction is true, vim-iced enable enhanced source extraction. This feature is enabled by default.

The difference from normal extraction is to extract source by not only def and defn but also let form.

borkdude/jet is required currently.
vim-iced will offer to download jet automatically then the command is run.

5.4. Use cases

Sometimes, it is useful to know how a function is used elsewhere. IcedUseCaseOpen command is for that.

IcedUseCaseOpen will open a separated buffer, and show the first usecase if it is found. When there are several usecases, IcedNextUseCase and IcedPrevUseCase commands switch the shown usecase.

5.5. clojure.spec

vim-iced provides following commands to integrate with clojure.spec.

Command Default key mapping Description



Browse specs, and show spec form in Document buffer.


Open a Document buffer, and show the spec form for specified keyword.
If no arguments are passed, the keyword under the cursor is used.


Open a Document buffer, and show the example of the spec.
If no arguments are passed, the keyword under the cursor is used.



See Checking spec for more information

5.6. Javadoc

vim-iced provides following commands to integrate with clojure.java.javadoc.

Command Description


Opens a browser window displaying the javadoc for a class_name.
Basically the result of clojure.core/class for the form under cursor is used.
For example, running this command over (clojure.java.io/resource "…​") will open a document page for java.net.URL.

6.1. Jump to definition

vim-iced provides IcedDefJump command for jumping to definition.

It supports jumping to

  • qualified keywords

  • local vars

  • protocol implementations (requires clj-kondo v2022.02.09 or later)

Can also specify using split window, tab, or other ways to open target location.

If you jumped to a definition with above command, vim-iced add the current cursor position to Vim’s tag stack. So you can go back easily with <C-t>.

See also:

6.2. Other navigations

IcedCycleSrcAndTest will cycle source file and test file for current namespace.

For example, when you are in foo.core, IcedCycleSrcAndTest command will open the file which has foo.core-test namespace. If there is no corresponding file, vim-iced suggests pseudo file path to create new namespace.

6.2.2. Cross reference

To browse positions referencing/depending on the form under cursor, following commands are useful.

Command Default key mapping





Such as foo.handler.home and foo.view.home, there are likely to have related namespaces in web applications.

IcedBrowseRelatedNamespace is useful to jump in these related namespaces.

6.2.4. Test cases

Sometimes you may want to jump to the test code that corresponds to a specific var.

IcedBrowseTestUnderCursor command will search test functions that contains the var name in its test name.

6.2.5. let form

IcedJumpToLet allows you to jump cursor to the nearest let form.

If let form is not found in current top list, cursor is not moved. Default key is mapped to <Leader>jl.

7. Macro

Expanding macro is important for writing/debugging macros. vim-iced provides following two commands.

Command Default key mapping





These commands will open a separated buffer, and show expanded result in it. IcedMacroExpand1OuterList will show the result of expanding once, while IcedMacroExpandOuterList will show the all expanded result.

7.1. Expand expanded macro

As you know, IcedMacroExpand1OuterList will show the result of expanding once.

In the separated buffer for expanding macro, you can use IcedMacroExpand1OuterList command and its <Leader>em key mapping again. This means you can expand the expanded macro additionally.

8. Testing

vim-iced can integrate with clojure.test, and provides following test commands.

Command Default key mapping Description



Run all tests in current project



Run tests in current namespace



Run a test under cursor
See also Related tests



Re run failed tests



Run the last test again

8.1. Test result

When tests are failed, vim-iced show error messages in Test buffer, and IcedTestBufferOpen command open the buffer.

vim-iced also set error positions to quickfix, and :cwindow command enables you to show this information. This is useful to jump each failed tests.

IcedTestUnderCursor is not just running a test under cursor.

If the var under cursor does not contain :test metadata and current namespace doesn’t end with "-test", vim-iced searches test vars in corresponding test namespace (c.f. Source and test) and run them. Target test vars should include original var name in its name.

8.3. Checking spec

There is also checking spec as one of the tests.

IcedTestSpecCheck command is useful to run clojure.spec.test.alpha/check for the function under cursor.

Adding test.check dependency is required.

You can specify the number of tests as a command argument, or g:iced#test#spec_num_tests variable.

8.4. Testing on plain nREPL

borkdude/jet is required currently.
vim-iced can download jet automatically if you want.

vim-iced basically requires nREPL with some middlewares. But in some case, you may need to use plain nREPL such as babashka.nrepl.

In this case, vim-iced provides simple test integration which has less functionality than cider-nrepl’s one.

To use this simple test integration, you don’t need to recognize whether your nREPL is plain or not. Following commands are also support simple test integration, so you just execute these commands as usual.

:IcedTestAll depends on current namespace. (e.g. foo.core will search all vars in foo.*).
So it’s still not really the whole test.

9. Formatting

vim-iced’s code formatting is powered by cljfmt. vim-iced proivdes the following two commands to format code.

Command Default key mapping Description



Reformat current form.



Reformat current buffer.

vim-sexp also provides a code formatting function. If you want to use vim-iced’s formatting function, you should define g:sexp_mappings as follows.

let g:sexp_mappings = {'sexp_indent': '', 'sexp_indent_top': ''}

9.1. Format on writing files

vim-iced also provides following sync commands.

These commands are useful for formatting on writing files. For example, add this to your confirguration to format the current file on write.

aug VimIcedAutoFormatOnWriting
  " Format whole buffer on writing files
  au BufWritePre *.clj,*.cljs,*.cljc,*.edn execute ':IcedFormatSyncAll'
aug END

Add this to your confirguration to format the current form on write.

aug VimIcedAutoFormatOnWriting
  " Format only current form on writing files
  au BufWritePre *.clj,*.cljs,*.cljc,*.edn execute ':IcedFormatSync'
aug END

9.2. Customize formatter

vim-iced also supports following (GraalVM powered) code formatting tools.

If you change g:iced_formatter option, vim-iced will use the tool to format code and calculate indent level.

These tools can be downloaded automatically if you want.

9.3. Auto indenting

vim-iced provides auto indentation, enabled by default.

The indent level is calculated by the set formatter, so cljfmt formatter which is used by default may lead slow down vim/nvim.

If you don’t use vim-iced’s auto indentation, you can disable it through the g:iced_enable_auto_indent variable.

let g:iced_enable_auto_indent = v:false

10. Debugging

vim-iced supports CIDER’s #dbg and #break reader literals. The easiest way is to put #dbg to your code, and evaluate it.

(defn fib [n]
  #dbg (loop [a 0 b 1 n n]
         (if (<= n 0)
           (recur b (+ a b) (dec n)))))

Once you evaluate (fib 10), debugger will launched something like follows.

debugging screenshot

10.1. Tracing function calls

vim-iced provides 2 commands for tracing function calls.

Command Description


Toggle tracing the specified var in current namespace.
If any symbol is not passed, the symbol under cursor is used.


Toggle tracing the specified namespace.
If any namespace is not passed, the current buffer’s namespace name is used.

Traced result will be displayed in Stdout buffer.

10.2. Browsing tapped values

If you have a big data structure and want to dig into it, browsing tapped values is useful.

When you evaluate (tap> YOUR_DATA), IcedBrowseTapped shows tapped values. Select the value you want to dig into, then browsing value start.

To use tap>, Clojure 1.10.0 or later is required.

All tapped values are stored in memory. So if you would like to delete them, execute IcedClearTapped command.

10.2.1. Browsing keys

For example, when there are following tapped values:

  • 1st tapped value: {:foo [{:dummy "bar"} {:bar "baz"}]}

  • 2nd tapped value: {:hello "world"}

The key to browse "baz" is 0 :foo 1 :bar.

  • The first 0 means "browse 1st tapped value"

  • :foo and :bar means the key for hash-map

  • 1 means the index of list/vector Back to top

11. Refactoring

11.1. Namespace

Following commands are available.

Command Default key mapping Description



Cleanup ns form.



Add require to ns form.



Add missing libspec.

If you don’t want to place a newline after the :require and :import tokens with IcedCleanNs command, Please see g:iced#refactor#insert_newline_after_require option.

11.1.1. Cache

Namespaces and its aliases are cached for performance. IcedClearNsCache will clear this cache.

11.2. Function

Following commands are available.

Command Default key mapping Description



Extract the form under cursor as a function.



Add an arity to defn, fn, defmacro, or defmethod.

11.2.1. Examples

IcedExtractFunction (📍 means cursor position, and "bar" is inputed by user)
;; before
(defn foo [x]
  (inc (📍* x 2)))

;; after
(defn- bar [x]
  (* x 2))

(defn foo [x]
  (inc (bar x)))
IcedAddArity (📍 means cursor position)
;; before
(defn foo [x]
  (📍inc x))
;; after
(defn foo
   (inc x)))

11.3. Form

Following commands are available.

Command Default key mapping Description



Convert current outer form to use threading macro.



Convert current outer form to use →> threading macro.



Move the form under cursor to nearest let binding.
If there is no let form in current top list, wrap the form with let.

11.3.1. Examples

;; before
(foo (bar (baz "hello")))
;; after
(-> "hello" baz bar foo)
;; before
(foo yy (bar xx "hello"))
;; after
(->> "hello" (bar xx) (foo yy))
IcedMoveToLet (📍 means cursor position, and "bar" is inputed by user)
;; before
(let [foo 1]
  (📍inc foo))
;; after
(let [foo 1
      bar (inc foo)]

11.4. Symbol

Following commands are available.

Command Default key mapping Description



Rename all occurrences of the symbol under cursor.

11.4.1. Examples

IcedRenameSymbol (📍 means cursor position, and "bar" is inputed by user)
;; before
(defn foo [x]
  (inc (* x 2)))

(def usage
  {:value (📍foo 21})

;; after
(defn bar [x]
  (inc (* x 2)))

(def usage
  {:value (bar 21})

12. Static analysis

Currently vim-iced supports static analysis by clj-kondo.

12.1. clj-kondo

This feature is disabled by default. To enable, set v:true to g:iced_enable_clj_kondo_analysis.

Analysis process is run at BufWritePost autocmd. Analyzed data is cached at g:iced_cache_directory, and used in followings:

12.1.1. Local analysis

From clj-kondo v2021.01.20, local analysis is supported. It can be used to support jumping to local vars.

This feature is disabled by default. To enable, set v:true to g:iced_enable_clj_kondo_local_analysis.

12.1.2. Tips

Cached files will be separated to var definition/usage and ns definition/usage automatically if you have jq or jet.

This leads to make IcedAddMissing and IcedAddNs more faster.

13. iced command

vim-iced provides iced command to use vim-iced easily. Main purpose of this command is launching REPL with all features for vim-iced.

13.1. Supports

iced command currently supports following tools.

The iced command will automatically search project files (such as "project.clj", "shadow-cljs.edn" and etc) and launch the appropriate REPL for the project.

When multiple projects are found, the priority is given in the order listed above.

If you want to specify project type, use --force-X option as below.

13.2. Usage

$ iced repl [options]

13.2.1. Options

Following options are specially treated by iced command.

Option Description


Enables ClojureScript features.
This option is enabled automatically when project configuration
file(eg. project.clj) contains 'org.clojure/clojurescript' dependency.

By default, it may be a false positive because of simple string match.

In this case, you can use more accurate checks with Babashka. (Optional and Limited for Leiningen and Clojure CLI only) iced command will use it automatically when bb command exists.


Enables features for testing with kaocha.


Force to use Boot.


Force to use Clojure CLI.


Force to use shadow-cljs.


Adds extra dependency.
For example: --dependency=iced-nrepl:0.4.3


Adds extra nrepl middleware.
For example: --middleware=iced.nrepl/wrap-iced


Launch instant REPL via Clojure CLI.
Instant REPL requires no project/config file.

Other options are passed to each program as below.

13.2.2. Arguments

Leiningen profile
$ iced repl with-profile +foo
Clojure CLI alias
$ iced repl -A:foo
Combinating several options
$ iced repl --with-cljs --force-clojure-cli -A:foo

13.3. Advanced

13.3.1. Clojure CLI project

iced command use clj command for Clojure CLI project by default.

If you would like to use clojure command instead, you can overwrite the command by ICED_REPL_CLOJURE_CLI_CMD environmental variable.

$ ICED_REPL_CLOJURE_CLI_CMD=clojure iced repl

For example, this is useful when you’d like to use rebel-readline in the REPL which is started by iced repl.

14. ClojureScript

vim-iced supports following environments currently.

vim-iced provides following commands for ClojureScript.

Command Description


Start CLJS REPL with specified environment.
REPL session changes to 'cljs.user' namespace.
If no environment is passed, g:iced#cljs#default_env is used.


REPL session changes to initial namespace.


Cycle clj/cljs session.
Error occurs when CLJS session does not exist.


Start CLJS REPL via user specified code.
This can be used to start any CLJS REPLs.
The code will be passed to cider.piggieback/cljs-repl.

14.1. Figwheel-main

To start CLJS REPL with Figwheel main, you need to specify build-id.

via IcedCljsRepl command
:IcedCljsRepl (figwheel.main.api/repl-env "build-id")
via IcedStartCljsRepl command
:IcedStartCljsRepl figwheel-main build-id

14.2. shadow-cljs

To start CLJS REPL with shadow-cljs, you need following steps.

iced command uses the existence of shadow-cljs.edn file for detecting shadow-cljs project for now. Thus, if you are using shadow-cljs via Leiningen, you need to setup manually (c.f. Manual Install) instead of the following steps.

  1. Start to watch

    • $ iced repl {YOUR-BUILD-ID}

      • In this case, iced command will start watching project instead of starting REPL.

      • You need a nREPL configuration file to start watching, but iced command can generate it automatically.

  2. Access shadow-cljs’s HTTP server in your browser

  3. Connect to nREPL

    • Execute IcedConnect command to connect to REPL

      • For shadow-cljs, cljs-repl will be started automatically after connection.

      • You don’t need to specify your build ID anymore.

If you would like to start CLJS REPL by hand, you can do it as follows.

:IcedEvalRepl (shadow.cljs.devtools.api/repl :YOUR-BUILD-ID)

15. Skeleton

vim-iced provides code skeleton when you open new clojure files. Concretely "when you open new" means the time when BufNewFile autocmd is fired.

Currently, following extensions are supported.

  • *.clj

  • *.cljs

  • *.cljc

15.1. Source file

For example, the skeleton when you open src/foo/bar.clj is as follows.

(ns foo.bar)

15.2. Test file

A skeleton for test file depends on the file extension.

15.2.1. *.clj

For example, the skeleton when you open test/foo/bar_test.clj is as follows.

(ns foo.bar-test
  (:require [clojure.test :as t]
            [foo.bar :as sut]))

15.2.2. *.cljs

For example, the skeleton when you open test/foo/bar_test.cljs is as follows.

(ns foo.bar-test
  (:require [cljs.test :as t :include-macros true]
            [foo.bar :as sut]))

15.2.3. *.cljc

For example, the skeleton when you open test/foo/bar_test.cljc is as follows.

(ns foo.bar-test
  (:require #?@(:clj  [[clojure.test :as t]
                       [foo.bar :as sut]]
                :cljs [[cljs.test :as t :include-macros true]
                       [foo.bar :as sut]])))

16. Operator

vim-iced provides following operators.

Operator Description


Evaluate codes
This operator supports Yank.


Evaluate code and print result to stdout buffer


Evaluate code and tap result by clojure.core/tap>


Evaluate code and replace it by the evaluation result


Evaluate code and add the result as a comment in current end of line

It is useful to combinate these operators with vim-sexp's motions like follows. (Change the mapping key as you like)

aug MyClojureSetting
  au FileType clojure nmap <buffer> <Leader>epe
      \ <Plug>(iced_eval_and_print)<Plug>(sexp_outer_list)``
  au FileType clojure nmap <buffer> <Leader>ept
      \ <Plug>(iced_eval_and_print)<Plug>(sexp_outer_top_list)``

  au FileType clojure nmap <buffer> <Leader>eae
      \ <Plug>(iced_eval_and_tap)<Plug>(sexp_outer_list)``
  au FileType clojure nmap <buffer> <Leader>eat
      \ <Plug>(iced_eval_and_tap)<Plug>(sexp_outer_top_list)``

  au FileType clojure nmap <buffer> <Leader>ere
      \ <Plug>(iced_eval_and_replace)<Plug>(sexp_outer_list)``
  au FileType clojure nmap <buffer> <Leader>ert
      \ <Plug>(iced_eval_and_replace)<Plug>(sexp_outer_top_list)``

  au FileType clojure nmap <buffer> <Leader>ece
      \ <Plug>(iced_eval_and_comment)<Plug>(sexp_outer_list)``
  au FileType clojure nmap <buffer> <Leader>ect
      \ <Plug>(iced_eval_and_comment)<Plug>(sexp_outer_top_list)``
aug END

17. Editing S-expression

vim-iced is depending on vim-sexp, and most features for editing S-expression are provided by vim-sexp.

In vim-iced, only the following functionalities which is extended from vim-sexp are provided.

Command Description


Slurp the next element.
If no elements in current form, search next form and slurp.
For searching next form, g:iced#paredit#slurp_max_depth is used.


Barf the last element in current form.

These commands are so useful when combined with vim-submode.
See Tips for more information.

18. Docker

When you are connecting to nREPL in docker container, various paths will be paths in the docker container.

Because some features such as jumping to definition does not work properly as it is, vim-iced provides g:iced#nrepl#path_translation option to translate these paths.

See Docker in Configuration section.

18.1. Example

At first, build a simple docker image for vim-iced.

Minimum dockerfile for vim-iced
FROM clojure:openjdk-14-lein-alpine
RUN apk add --no-cache --virtual .builddeps git && \
    git clone --depth 1 --single-branch https://github.com/liquidz/vim-iced /usr/local/src/vim-iced && \
    apk del .builddeps
ENV PATH $PATH:/usr/local/src/vim-iced/bin
ENTRYPOINT ["iced", "repl"]
$ docker build -t vim-iced .

Second, launch nREPL with a fixed port number.

(defproject foo "0.1.0-SNAPSHOT"
  ;; ...

  ;; Change port number as you like
  :repl-options {:host "" :port 55555}

  ;; ...
$ docker run --rm -it --expose 55555 -p 55555:55555 -v $(pwd):/tmp vim-iced

Finally just connect with IcedConnect as usual.

19. Command palette

It is hard to remember all commands by vim-iced since there are many commands.

But most commands can be filtered/selected from the command palette even if you don’t remember them.

Command Default key mapping Description



Select a command from the palette.

If you’d like to customize the command palette(e.g. adding new command), g:iced#palette option is useful.

See Command palette in Configuration section for more information.

20. Buffer

20.1. Stdout buffer

When evaluating a form the output from Clojure’s standard output is sent to a dedicated buffer. Vim-iced provides the following commands to control it’s stdout buffer.

Command Default key mapping Description



Open stdout buffer.



Open stdout buffer.



Clear all lines in stdout buffer.



Close stdout buffer window.

20.1.1. Notification

In addition to the stdout buffer, standard output can be displayed on popup notification when stdout buffer is invisible. If you don’t use notification for standard output, see g:iced#buffer#stdout#enable_notify option.

See also vim-iced-notification for more information.

20.1.2. Delimiters

It is useful to have a delimited line in the unit of output to limit the range of what you want to see.

See g:iced#buffer#stdout#enable_delimiter for more information.

20.2. Test buffer

Test results will be outputted to dedicated buffer.

vim-iced provides following commands to control test buffer.

Command Default key mapping Description



Open test result buffer.

Test buffer is opened automatically when tests are failed. On the other hand, test buffer is closed automatically when test are succeeded.

So you don’t need to use the above command basically.

20.3. Document buffer

Documents will be shown to popup window by default. But you can show them to dedicated buffer to keep them displayed.

vim-iced provides following commands to control document buffer.

Command Default key mapping Description



Open a window, and show documents for specified symbol.
If symbol is not passed, the symbol under the cursor is used.



Close document window.

21. Configuration

21.1. Evaluation

21.1.1. Eval inside comment

comment form is useful during development for checking behavior. You can use evaluating outer list range to evaluate codes inside comment form, but it is cumbersome as some cursor movements are required.

IcedEvalOuterTopList helps by selecting different code to be evaluated when the current top form is a comment. If the cursor is in a form nested inside the comment, the code to be evaluated will be the cursor’s topmost form in the comment, rather than the comment form itself.

When the cursor is otherwise at the same depth as the comment form itself, the comment will be evaluated as a do form. This can be configured by g:iced#eval#inside_comment, and it is enabled by default.

Example (📍 means cursor position)
  (print (📍str "hello"))   ;; Execute `:IcedEvalOuterTopList`
  (print (str "world")))    ;; => hello

📍(print (str "hello"))     ;; Execute `:IcedEvalOuterTopList`
  (print (str "world")))    ;; => helloworld

21.2. Formatting

21.2.1. Customizing indent rules

g:iced#format#rule is a configuration that allow you to define indentation rules for cljfmt.

For example, merr's let has a form like follows.

(merr/let +err+ [foo 1]
          (inc foo))

To format like clojure.core/let, you should define the following rule.

let g:iced#format#rule = {
      \ 'merr.core/let': '[[:block 2] [:inner 1]]',
      \ }

Then you can get the following result.

(merr/let +err+ [foo 1]
  (inc foo))

See cljfmt’s README for more information.

21.2.2. Tonsky’s formatting rule

If you’d like to use Tonsky’s formatting rule, you can use this rule with cljfmt and cljstyle.


Set the following rule to g:iced#format#rule option.

let g:iced#format#rule = {'#"^\w"': '[[:inner 0]]'}

Add following :indents setting to your .cljstyle.

 :indents ^:replace {#"^\w" [[:inner 0]]}

21.2.3. Extra configuration for cljfmt

While g:iced#format#rule is a option for indentation rules, g:iced#format#options is a option for cljfmt’s other configurations such as :remove-surrounding-whitespace?, :remove-trailing-whitespace? and so on.

let g:iced#format#options = {
      \ 'remove-consecutive-blank-lines?': v:false,
      \ 'remove-multiple-non-indenting-spaces?': v:true,
      \ }

See cljfmt’s README for more information.

21.3. Hook

Some events on vim-iced can be hooked, and you can execute any processing.

g:iced#hook is the definition. This is a dictionary with hook event as a key, and the value is a dictionary or a list of dictionaries.

Definition example
let g:iced#hook = {
    \ 'connected': {'type': 'function',
    \               'exec': {_ -> iced#message#info_str('foo')}},
    \ 'disconnected': [{'type': 'command',
    \                   'exec': 'echom "bar"'}],
    \ 'ns_required': {'type': 'eval',
    \                 'exec': '(println "baz")'},
    \ 'test_finished': {'type': 'shell',
    \                   'exec': {v -> printf('tmux display-message "%s"', v.summary)}}
    \ }

Another way, you can also use iced#hook#add function to add definitions.

call iced#hook#add('disconnected', {
      \ 'type': 'command',
      \ 'exec': 'echom "hello"',
      \ })

21.3.1. Event

Following events can be hooked.

Event key Event parameters Description


[{'label': 'nREPL', 'type': 'nrepl', 'port': 12345}]

Hooked before the start of connection.
This hook only allows function hook type.
Reporter should return a list of dictionary which has same parameters.


host: Host name to connect
port: Port number to connect

Hooked just before connecting.
If any hook functions return {'cancel': 'message'}, vim-iced will cancel to connect.



Hooked when vim-iced is connected to nREPL.



Hooked when vim-iced is disconnected from nREPL.


result: Test result ("succeeded" or "failed")
summary: Test summary string

Hooked when running test is finished.



Hooked when :IcedRequire is finished.



Hooked when :IcedRequireAll is finished.


session: Destination session type. "clj" or "cljs".

Hooked when current session is switched.


code: The code to be evalated.
option: Evaluation option.

Hooked before the start of evaluation.
The option parameter contains id, file, line, column, verbose and so on.


result: Result map respnsed by nREPL server.
option: Evaluation option.

Hooked after evaluation.

The result parameter contains id, status, ns, value and so on.
The option parameter contains line, column, verbose, code and so on.

21.3.2. Reporter

Hooked events and event parameters are reported to reporters. You can define some kind of reporters as follows.

Reporter key Description


Process a event by a shell command.
The shell command is executed in terminal.


Process a event by IcedEval command.


Process a event by a user-defined function.


Process a event by a vim command.

21.4. Docker

g:iced#nrepl#path_translation is a option to define path translation rules.

In actual use, it is recommended to define the option in the project-specific configuration file by vim-localrc like follows.

let g:iced#nrepl#path_translation = {
      \ '/tmp': expand('<sfile>:p:h'),
      \ '/root': $HOME,
      \ }

21.5. Command palette

The g:iced#palette option allows you to customize the command palette.

In actual use, it is recommended to define the option in the project-specific configuration file by vim-localrc like follows.

let g:iced#palette = {
      \ 'foo bar': ':IcedEval (+ 1 2 3 4 5)',
      \ }

21.6. Advanced settings

21.6.1. Stdout buffer

Option Default value Description



Buffer position definition.
Following values are supported.

  • vertical

  • leftabove

  • aboveleft

  • rightbelow

  • belowright

  • topleft

  • botright



If not empty, vim-iced write stdout buffer content to the file path.



Max line count to buffer for g:iced#buffer#stdout#filer.



Max line count to keep in stdout buffer.
If negative number is setted, it means no limit.



Delay time for deleting excess lines in stdout buffer.
Unit is milli sec.
This option is enabled when g:iced#buffer#stdout#max_line is a positive number.

21.6.2. Document

Table 3. Document buffer
Option Default value Description



Buffer position definition.
Following values are supported.

  • vertical

  • leftabove

  • aboveleft

  • rightbelow

  • belowright

  • topleft

  • botright



Document window height.



If v:true and document buffer is visible, update document buffer with document for the form under cursor.

Table 4. Auto document
Option Default value Description



Enable automatic displaying one-line document.
This will be done by CursorMoved / CursorMovedI auto command and timer.

The value should be one of 'normal', 'insert' or 'any'.
Otherwise, this option will be disabled.

  • 'normal': Enabled on only normal mode.

  • 'insert': Enabled on only insert mode.

  • 'every' : Enabled on normal and insert mode.



Display documents on popup window.
The value should be one of follows.
Otherwise, this option will be disabled.

  • 'full' : Enabled on only full document. IcedDocumentOpen shows full document.

  • 'one-line': Enabled on only one-line document.

  • 'every' : Every enabled.



Max line distance to display one-line document.
See also g:iced_enable_auto_document.



Delay time for displaying one-line document.
Unit is milli sec.

21.6.3. ClojureScript

Option Default value Description



If v:true, enable automatic switching CLJ/CLJS session.
This will be done by WinEnter auto command.



If v:true, enable enhanced ClojureScript completion.

22. Tips

22.1. vim-submode

vim-submode is a Vim plugin to provide the way to define "submodes" to the built-in vim-modes.

vim-submode is useful to repeat some commands such as IcedSlurp or IcedBarf.

Repeat slurping and barfing
" Change key mappings as you like.
" In this case, type `<LocalLeader>kssss` to repeat slurping.
call submode#enter_with('slurp', 'n', '', '<LocalLeader>ks', ':<C-u>IcedSlurp<CR>')
call submode#enter_with('slurp', 'n', '', '<LocalLeader>kb', ':<C-u>IcedBarf<CR>')
call submode#leave_with('slurp', 'n', '', '<Esc>')
call submode#map('slurp', 'n', '', 's', ':<C-u>IcedSlurp<CR>')
call submode#map('slurp', 'n', '', 'b', ':<C-u>IcedBarf<CR>')
Repeat jumping errors
" Change key mappings as you like.
" In this case, type `<LocalLeader>nnnn` to repeat jumping next errors.
call submode#enter_with('sign_jump', 'n', '', '<LocalLeader>n', ':<C-u>IcedJumpToNextError<CR>')
call submode#enter_with('sign_jump', 'n', '', '<LocalLeader>N', ':<C-u>IcedJumpToPrevError<CR>')
call submode#leave_with('sign_jump', 'n', '', '<Esc>')
call submode#map('sign_jump', 'n', '', 'n', ':<C-u>IcedJumpToNextSign<CR>')
call submode#map('sign_jump', 'n', '', 'N', ':<C-u>IcedJumpToPrevSign<CR>')

22.2. Auto connection

If you’d like to connect to REPL automatically, define autocmd for VimEnter event.

It is recommended to define autocmd like follows in ftplugin directory.

Example: $HOME/.vim/ftplugin/clojure.vim
aug MyClojureSetting
  au VimEnter * IcedConnect
aug END

If you don’t want to connect to REPL for project.clj, *.edn, or etc, skip connecting as below.

Example: $HOME/.vim/ftplugin/clojure.vim
function! s:auto_connect() abort
  if expand('%:t') ==# 'project.clj' || expand('%:e') ==# 'edn'

aug MyClojureSetting
  au VimEnter * call s:auto_connect()
aug END

22.3. Overwriting mappings

vim-iced provides default key mappings. Since vim-iced will verify the mapping existence, if you would like to overwrite some key mappings, you could do as follows.

Example: $HOME/.vim/ftplugin/clojure.vim
nmap <Leader>em <Plug>(iced_eval_at_mark) (1)
nmap <Leader>eM <Plug>(iced_macroexpand_1_outer_list) (2)
nmap <Nop>(iced_macroexpand_outer_list) <Plug>(iced_macroexpand_outer_list) (3)
1 Use <Leader>em for iced_eval_at_mark instead of <Leader>ea.
2 Use <Leader>eM for iced_macroexpand_1_outer_list instead of <Leader>em.
3 Disable default mapping <Leader>eM for iced_macroexpand_outer_list.

22.4. Disabling paredit

By default vim-sexp's paredit mode is enabled. If you want to be able to add parentheses individually, you can disable that behavior by adding the following line to one of your startup files:

let g:sexp_enable_insert_mode_mappings=0

If you also want to turn off other vim-sexp key mappings for clojure files (and scheme, lisp, and timl files), add the following line to one of your startup files instead of the preceding one:

let g:sexp_filetypes=''

Then specify any vim-sexp key mappings that you want to keep by following the the instructions at :help sexp-explicit-mappings. Place the resulting code (including the augroup block) into a startup file.

22.5. Reloaded workflows

If you are managing lifecycle of components with Stuart Sierra’s component, integrant or etc, following configurations are useful.

22.5.1. CIDER way

vim-iced provides IcedRefresh command as same as the cider-ns-refresh feature. And it also support before/after hooks like follows.

let g:iced#nrepl#ns#refresh_before_fn = 'user/stop'
let g:iced#nrepl#ns#refresh_after_fn = 'user/start'

With this configuration, you can insert executions of functions before and after refreshing files when you execute IcedRefresh command.

22.5.2. Manual way

aug MyClojureSetting
  " Change key mappings and forms as you like
  au FileType clojure nnoremap <buffer> <Leader>go :<C-u>IcedEval (user/go)<CR>
  au FileType clojure nnoremap <buffer> <Leader>Go :<C-u>IcedEval (user/reset)<CR>
aug END

23. External integration

23.1. kaocha

kaocha is full featured next gen Clojure test runner.

vim-iced has a external plugin named vim-iced-kaocha to integrate with kaocha.

Command Description


Run tests specified by {ids}.
{ids} must be an array of testable-id.
See kaocha document for testable-id.


Run a test under cursor.


Run tests in current namespace.


Run all tests in current project.


Re run failed tests.


Run last test again.

23.1.1. Usage

First, add vim-iced-kaocha to your vim-plug section.

call plug#begin('~/.vim/plugged')

Plug 'liquidz/vim-iced', {'for': 'clojure'}
" Add this line >>>>>>>>>>>>>>
Plug 'liquidz/vim-iced-kaocha'
" <<<<<<<<<<<<<<<<<<<<<<<<<<<<

call plug#end()

Next, launch your REPL with --with-kaocha option.

$ iced repl --with-kaocha
Key mapping example
aug MyClojureSetting
  " Change key mappings as you like.
  au FileType clojure nmap <silent><buffer> <Leader>ktt <Plug>(iced_kaocha_test_under_cursor)
  au FileType clojure nmap <silent><buffer> <Leader>ktn <Plug>(iced_kaocha_test_ns)
  au FileType clojure nmap <silent><buffer> <Leader>ktr <Plug>(iced_kaocha_test_redo)
  au FileType clojure nmap <silent><buffer> <Leader>ktl <Plug>(iced_kaocha_test_rerun_last)
aug END

24. Socket REPL

vim-iced targets nREPL mainly, but now will also support minimal operations on socket REPL.

Table 5. Socket REPL supports
Feature Supports? Description

All ranges to evaluation are supported.

Only referring docstrings.


Any other features are not supported.

vim-iced provides following commands to connect socket REPL.

Command Description


Make connection to socket REPL.


Make connection to pREPL.
This connection is based on socket repl connection.

24.1. Connecting socket REPL

You can try socket REPL connection as follows.

Table 6. Launch socket REPL via babashka
Tool Command

$ bb --socket-repl 5555

$ joker --repl :5555

$ planck --socket-repl 5555

$ lumo --socket-repl 5555

Connect to socket REPL
:IcedConnectSocketRepl 5555

Another way, you can launch socket REPL with random empty port internally.

:IcedInstantConnect babashka

If you’d like to use nREPL server instead of Socket REPL, you use g:iced#repl#babashka_repl_type option.

24.2. Connecting pREPL

pREPL connection is based on socket REPL connection in vim-iced.

borkdude/jet is required currently.
vim-iced can download jet automatically if you want.

You can try pREPL connection as follows.

Launch pREPL
$ clj -J-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/io-prepl}"
Connect to pREPL
:IcedConnectPrepl 5555

25. Developer

25.1. Minimal configuration

Minimal configuration is useful for debugging. If you find an unexpected behavior, please try this configuration.

$ mkdir -p /tmp/vim-iced-test/pack/iced/start
$ git clone https://github.com/guns/vim-sexp \
$ git clone https://github.com/liquidz/vim-iced \
set nocompatible
set encoding=utf-8
scriptencoding utf-8
filetype plugin indent on

set runtimepath-=$HOME/.vim
set runtimepath-=$HOME/.vim/after
set runtimepath-=$HOME/.config/nvim
set runtimepath-=$HOME/.config/nvim/after
set runtimepath-=$HOME/.local/share/nvim/site
set runtimepath-=$HOME/.local/share/nvim/site/after

set packpath=/tmp/vim-iced-test
Lanuch Vim/Neovim
# Vim
$ vim -u /path/to/test_config.vim /path/to/your/project/code.clj

# Neovim
$ nvim -u /path/to/test_config.vim /path/to/your/project/code.clj

25.2. Testing vim-iced

vim-iced uses vim-themis as a test runner.

25.2.1. Run tests on docker

The easiest way to run tests is the way using docker.

# Run vimscript test for vim
$ make docker_themis

# Run vimscript test for neovim
$ make docker_neovim_themis

If you would like to filter test cases, use --target option for vim-themis. Please refer to the argument of themis#suite in the first line of each test for the string to be specified for --target.

For example, execute the following command to run test cases only for iced#nrepl#eval.

$ docker run --rm -v $(pwd):/root \
      --entrypoint './.vim-themis/bin/themis' uochan/vim:latest \
      --target 'iced\.nrepl\.eval '

25.2.2. Run tests locally

You can also run tests locally. The following make rule will clone vim-themis and run tests.

# Run vimscript test
$ make themis

# Run all tests
$ make test

# Once you clone vim-themis, you can specify `--target` like below
$ ./.vim-themis/bin/themis \
      --target 'iced\.nrepl\.eval '

26. Cheatsheet

Based on default key mappings.
See help file for whole default key mappings.



Make connection to nREPL


Reformat the form under cursor


Evaluate outer list


Evaluate outer top list


Require current namespace


Interrupt code evaluation


Reload all changed files


Evaluate macroexpand-1 for outer list


Evaluate macroexpand for outer list


Run test under cursor


Run tests in current namespace


Re run failed tests


Re run last test


Run all tests


Jump cursor to the definition of symbol under cursor


Jump back cursor


Browse namespaces related to the current namespace


Show documents for the symbol under cursor


Show source for the symbol under cursor


Close document buffer


Browse specs


Clean namespace


Add ns to require form


Add missing libspec


Extract the form under cursor as a function


Add another arity


Convert form to use -> threading macro.


Convert form to use ->> threading macro


Move the form under cursor to nearest let binding


Rename all occurrences of the symbol under cursor.



Show command palette