Conjure (Clojure開発環境) 導入メモ

# はじめに

Clojure (clojure-cli) を、Neovim用プラグインである[Conjure](https://github.com/Olical/conjure)で動かす。

# 導入方法 (Neovim/Plug)
NeovimとClojure CLI Toolsは、既に入っている前提。
```lua
-- init.lua
vim.g.maplocalleader = ','
vim.cmd([[
call plug#begin()
  Plug 'tpope/vim-dispatch'
  Plug 'clojure-vim/vim-jack-in'
  Plug 'radenling/vim-dispatch-neovim'
  Plug 'Olical/conjure'
call plug#end()
]])
```
1. 上記を`init.lua`に記載した状態で`:PlugInstall`する。
    - `maplocalleader`の文字は任意。
    - `maplocalleader`の設定は、`Plug`より前で行う必要がある。
2. `:Clj`でclojure-cliのREPLを起動。
    - 起動後は、`C-\ C-n`でREPLから抜ける＆他のバッファに変えてしまってもOK。
    - このREPLは裏側で動いているだけで、基本的には使わない。
3. 適当な`.clj`ファイルを開くと「REPLに接続しました」のポップアップが出て、Conjureが利用可能になる。
    - 上手くつながらなかった場合は、`:ConureConnect`で手動でつなげる。

# 基本操作
| コマンド | 機能 |
|:-------:|:---:|
| `,ee` | カーソルが入っている括弧を評価 |
| `,eb` | バッファを評価 |
| `,E` | (ビジュアルモードで) 選択範囲を評価 |
| `,lv` | ウィンドウを縦に分割して、REPLを表示 |
| `,lq` | 分割されたウィンドウを閉じる |

※ 上記の`,`は`<maplocalleader>`。「導入方法」の設定によって読み替える。
※ 他の操作は[マニュアル (英語)](https://github.com/Olical/conjure/blob/master/doc/conjure.txt) 参照
※ `:verbose :map ,`でも、`,`から始まるキーマップを調べられる。

# 追加設定

## 自動補完
coc.nvim / asyncomplete.vim / nvim-cmp が利用可能であり、Conjure開発者がnvim-cmpを推奨しているため、これを設定。`lspconfig`の設定で、`clojure-lsp`ではなく`clojure_lsp`である点に注意。
```lua
vim.cmd([[
call plug#begin()
  Plug 'neovim/nvim-lspconfig'
  Plug 'hrsh7th/cmp-nvim-lsp'
  Plug 'hrsh7th/cmp-buffer'
  Plug 'hrsh7th/cmp-path'
  Plug 'hrsh7th/cmp-cmdline'
  Plug 'hrsh7th/nvim-cmp'
  Plug 'PaterJason/cmp-conjure'
call plug#end()
]])

local cmp = require'cmp'
cmp.setup({
  snippet = {
    expand = function(args)
      vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users.
    end,
  },
  window = {
  },
  mapping = cmp.mapping.preset.insert({
    ['<C-b>'] = cmp.mapping.scroll_docs(-4),
    ['<C-f>'] = cmp.mapping.scroll_docs(4),
    ['<C-Space>'] = cmp.mapping.complete(),
    ['<C-e>'] = cmp.mapping.abort(),
    ['<CR>'] = cmp.mapping.confirm({ select = true }),
  }),
  sources = cmp.config.sources({
    { name = 'nvim_lsp' },
  }, {
    { name = 'buffer' },
  })
})
cmp.setup.cmdline({ '/', '?' }, {
  mapping = cmp.mapping.preset.cmdline(),
  sources = {
    { name = 'buffer' }
  }
})
cmp.setup.cmdline(':', {
  mapping = cmp.mapping.preset.cmdline(),
  sources = cmp.config.sources({
    { name = 'path' }
  }, {
    { name = 'cmdline' }
  })
})
local capabilities = require('cmp_nvim_lsp').default_capabilities()
require('lspconfig')['clojure_lsp'].setup {
  capabilities = capabilities
}
```
[下記](https://clojure-lsp.io/installation/)でclojure-lspの本体も入れる。
```bash
# Mac/Linux
brew remove clojure-lsp # if you have old clojure-lsp installed via brew
brew install clojure-lsp/brew/clojure-lsp-native

# Script
sudo bash < <(curl -s https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/master/install)

# Windows
scoop install clojure-lsp
```

## Vim起動時に、REPLも自動起動させる
Vimを立ち上げる度に`:Clj`を打つのが手間ならば、下記で自動化できる。`'^['`の部分は`^`と`[`を打ち込むの**ではなく**、`<C-v><Esc>`をインサートモードで入力することに注意。
```lua
vim.api.nvim_create_autocmd({'VimEnter'}, {
  pattern = {'*'},
  callback = function()
    vim.cmd([[
      Clj
      close!
      call feedkeys('^[')
    ]])
  end
})
```

## neovim-session-managerと併用する
[neovim-session-manager](https://github.com/Shatur/neovim-session-manager)等のセッション管理系のプラグインが入っているとREPLもそのプラグインに保存され、次回起動時に復元されてしまう。上述のREPL自動起動は復元がなされているかどうかに関わらず新たにREPLを開いてしまうため、バッファがREPLであふれてしまう。
これでは具合が悪いので、下記のコードで①Vim終了時のREPLクローズと、②セッション復元後にREPL自動起動を行うようにする。
```lua
-- ①Vim終了時にREPLを閉じる
vim.api.nvim_create_autocmd({'ExitPre'}, {
  pattern = {'*'},
  callback = function()
    vim.cmd('bd! term://')
  end
})

-- ②REPL起動はセッション復元後 (前述コードの'VimEnter'が'SessionLoadPoist'になっている)
vim.api.nvim_create_autocmd({'SessionLoadPost'}, {
  pattern = {'*'},
  callback = function()
    vim.cmd([[
      Clj
      close!
      call feedkeys('')
    ]])
  end
})
```

## ポップアップの色を変える
ctermで利用できる色は、例えば[こういったサイト](https://www.ditig.com/256-colors-cheat-sheet)で調べられる。
```lua
vim.cmd('highlight NormalFloat ctermbg=231')
```

## ポップアップの位置を変える
私は[noice.vim](https://github.com/folke/noice.nvim)を通知機能付きで使っているので、ポップアップが右上に出ると干渉する。そこで、ポップアップを右下に出るようにする。なお、ポップアップの設定を`vim.cmd('let g:conjure#log#hud#enabled = 0')`で消せるとヘルプに書いてあるのだが、私の環境では機能しなかった。
```lua
vim.cmd('let g:conjure#log#hud#anchor = "SE"')
```


## 画面分割時に、REPLを右側に表示する
```lua
vim.cmd('let g:conjure#log#botright = 1')
```

# 同機能プラグインの紹介
同じような機能を持つプラグインに、[fireplace.vim](https://github.com/tpope/vim-fireplace)、[vim-iced](https://github.com/liquidz/vim-iced)、[spacevim]()のclojureレイヤーの3つがある。
**vim-fireplace**はミニマムな機能を提供するもので、プラグイン操作が分かりやすい。ただし、ある値・関数を評価すると、その結果が返るまでVimが操作を受付けなくなるのが難点。**vim-iced**はその反対で、プロジェクト向けの機能が充実しているイメージ。"Asynchronous evaluation"ということで、時間がかかる評価をしてもVim上で編集を続けられる。REPLのデザイン・挙動が少しユニークに思えた。**spacevim**は機能の導入が非常に楽な代わりに、使っていて分かるレベルでVimの挙動が重くなる。**Conjure**はと言うと、[開発者がclojure-cliを好きと言っており](https://github.com/Olical/conjure/wiki/Quick-start:-Clojure)、全体的にシンプル[^1]。
fireplace.vimの開発者は[「Vim・プラグイン・アーティスト」を自称して](https://twitter.com/tpope)いて、vim-icedの場合は[企業でClojureプロジェクトを進めているVimmer (日本人)](https://twitter.com/uochan)。Conjureは[Lispラブなプログラマ](https://twitter.com/OliverCaldwell)。spacevimは[コミュニティ駆動]()。各プラグインで開発者の特色が出ていて面白い。
私は1ファイルに収まるようなちょっとしたコードを書くためにClojureを利用しているので、今のところ、Conjureがちょうど良い。

# デモ (英語)
`curl -fL conjure.fun/school | sh`を打つと基本的操作のデモを、(`curl`/`sh`が動くなら) インストールなしで体験できる。これがなかなか良くできていて、このデモのおかげでConjureを利用しようと思えた。

# 参考
- [helpマニュアル1](https://github.com/Olical/conjure/blob/master/doc/conjure.txt)
- [helpマニュアル2](https://github.com/Olical/conjure/blob/master/doc/conjure-client-clojure-nrepl.txt)

[^1]: 感覚的には、fireplace.vim=ミニマム、vim-iced=多機能、spacevim=イージー、conjure=シンプル。fireplace.vim以外は、"Asynchronous evaluation"の機能を持つ。