Skip to content

Instantly share code, notes, and snippets.

@Olical
Last active October 7, 2019 11:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Olical/8ddc726c41112be5eb450b12954d81f0 to your computer and use it in GitHub Desktop.
Save Olical/8ddc726c41112be5eb450b12954d81f0 to your computer and use it in GitHub Desktop.
Using a super alpha version of Conjure - Clojure socket prepl in Neovim

Conjure alpha preview

Neovim Clojure tooling over prepl

This is the legacy Rust implementation that I've replaced with Clojure. Check out the master branch for the main Clojure version. The old source can be found on the legacy-rust-implementation branch.

I've been working on Conjure for a while now and using it day to day at work. It's coming along but I still have a todo list that stretches off the screen. This is mostly "nice to haves", not essential features. I don't want to release it until I've completed my main list and I'm happy with the level of polish.

This gist covers how to set up the current super alpha version (v0.3.0) in your own Neovim so you can give it a go and maybe give me some feedback. Here's the caveats:

  • Requires vim-sexp for a couple of the bindings to select forms. (I'll remove this requirement soon)
  • It's not configurable.
  • It's not documented.
  • You need to install the Rust toolchain. (don't worry, it's easy)
  • It requires Clojure 1.10.0 or greater.
  • ClojureScript errors explode right now, my patch to fix this is merged but not released yet.

So with all of that in mind, I'd love for you to take it for a spin and let me know what you think!

It's worth noting that my design philosophy for the entire project is to get 90% of the value of similar tools with 10% of the effort. It won't be as good as whatever else is out there but it will require almost no setup or dependencies and keep working forever and a day without changes (hopefully). I'm not going for a sprawling feature list, I'm going for stability and polish of a core set of features that you can extend yourself by simply setting up bindings to eval things for you.

Installing the plugin

I use [vim-plug][] for plugin management so you'll have to adapt this to your preferred system.

Plug 'Olical/conjure', { 'tag': 'v0.3.0', 'do': 'bash install.sh' }

" This requirement will be replaced eventually.
Plug 'guns/vim-sexp' | Plug 'tpope/vim-sexp-mappings-for-regular-people'

" I use this to set up project specific bindings such as <localleader>rA to connect to my API server.
Plug 'embear/vim-localvimrc'

I'm currently pinning my version to commits instead of tags, so you can also use 'commit' instead of 'tag' if you're feeling brave.

Plug 'Olical/conjure', { 'commit': 'fa8495a26cb3207abf11008542ee81d8f8c6f56b', 'do': 'bash install.sh' }

Once you :PlugUpdate it should be ready to go. You'll probably want to disable things like vim-fireplace while you're trying this out.

Starting your prepl

You can start prepls from the CLI like this:

clj -J-Dclojure.server.jvm="{:port 5555 :accept clojure.core.server/io-prepl}" \
    -J-Dclojure.server.node="{:port 5556 :accept cljs.server.node/prepl}" \
    -J-Dclojure.server.browser="{:port 5557 :accept cljs.server.browser/prepl}"

Which starts a JVM, node and browser socket based prepl at the same time. Feel free to delete the parts you don't care about! Alternatively you can define a namespace inside your project and call it from your main function (which I use at work):

(ns my.project.prepl
  (:require [clojure.core.server :as server]))

(defn start-prepl! [{:keys [bind port name]}]
  (server/start-server {:accept 'clojure.core.server/io-prepl
                        :address bind
                        :port port
                        :name name}))

Connecting

Once you have Conjure installed and your prepl server started you can connect to it like so:

call conjure#connect("api", "127.0.0.1:5555", "cljc?$", "clj")
call conjure#connect("ui", "127.0.0.1:5557", "clj(s\|c)$", "cljs")

You can find all of the function in the autoload/conjure.vim. Here's the arguments for conjure#connect in order:

  • The name of the connection, it identifies it in the log buffer that opens at the bottom of your screen. If you have multiple connections this lets you know where each response came from.
  • Obviously the host and port for the socket prepl.
  • Regex the file you're working in has to match to use this connection. This is so that you can specify different connections for Clojure and ClojureScript. Or even different connections for different directories!
  • The language you're using on this connection, either clj or cljs. I think I can get rid of this when one of my patches is merged and released in Clojure 1.11.

In this example, because I've opened up two connections which overlap on cljc files, any evaluation I perform from within a cljc file will be sent to both environments at the same time. This helps keep your evaluations in sync across your Clojure and ClojureScript if you share cljc files.

Using Conjure

Once you're connected you have the following bindings available to you:

  • <localleader>rp - print the current connection list to the log buffer.
  • <localleader>rl - show the log buffer if you've closed it.
  • cp... - evaluate the following motion, with vim-sexp you can use cpaf to evaluate a form for example.
  • cpp - same as cpaf, just a handy shortcut.
  • <localleader>re - same as cpaF, which is the outermost form in vim-sexp land.
  • <localleader>rf - evaluate the file from disk.
  • <localleader>rb - evaluate the buffer.
  • <localleader>rt - run the buffer's tests.
  • <localleader>rT - run all tests.
  • <localleader>rr - Use [org.clojure/tools.namespace "0.3.0-alpha4"] to reload changed namespaces.
  • <localleader>rR - Similar to rr but it'll clear the cache and reload everything.
  • K - print documentation for the symbol under the cursor.
  • gd - go to the definition of the symbol under the cursor.

Feedback please!

If you start using Conjure I'd love to know how it worked for you. My next feature on the list is super rudimentary autocomplete (which I almost have done) (DONE! It works via omnicomplete, so <C-x><C-o> should trigger it, or whatever completion plugin you use) then there's a bunch of "nice to haves" as well as stabilisation, customisation and documentaiton.

If you stick with it I'll be sure to get you continual versioned improvements that hopefully will never break your workflow or require dependencies. You can watch the repo for releases to keep track of the progress if you'd like. Chuck a star on the repo to show me that you're at least slightly interested.

I hope you find Conjure useful and I look forward to making it better and better (not bigger and bigger) over the coming months and years.

@sogaiu
Copy link

sogaiu commented Feb 27, 2019

Nice!

Here's a summary of what worked:

  • <localleader>rp - print the current connection list to the log buffer.
  • <localleader>rl - show the log buffer if you've closed it.
  • cp... - evaluate the following motion, with vim-sexp you can use cpaf to evaluate a form for example.
  • cpp - same as cpaf, just a handy shortcut.
  • <localleader>re - same as cpaF, which is the outermost form in vim-sexp land.
  • <localleader>rf - evaluate the file from disk.
  • <localleader>rb - evaluate the buffer.
  • K - print documentation for the symbol under the cursor.
  • gd - go to the definition of the symbol under the cursor.
  • <C-x><C-o> - omnicomplete

and what didn't:

  • <localleader>rt - run the buffer's tests.
  • <localleader>rT - run all tests.

and not sure:

  • <localleader>rr - Use [org.clojure/tools.namespace "0.3.0-alpha4"] to reload changed namespaces.
  • <localleader>rR - Similar to rr but it'll clear the cache and reload everything.

For the "didn't work" section, I see the following in the log:

;; [api] err @ 08:00:53
;; Syntax error compiling var at (REPL:90:13).
;; Unable to resolve var: clojure.test/*test-out* in this context
;; 

FWIW, manually running lein test from the command line does work.

For the "not sure" section, need to further test if this is something with the local project or setup.

In any case, thanks for your work on this!

@Olical
Copy link
Author

Olical commented Mar 7, 2019

Thanks for giving it a try! I'm currently hacking away on a rewrite in Clojure that should be a lot more robust and user friendly. The Rust version (this one) is more than good enough and I'm still using it at work every day. I won't be writing up docs / blog posts until I'm done with this rewrite though.

I'm taking everything I've learned from experimenting through Rust (and ClojureScript) and channelling it into this third attempt. I hope the end result will be worth it and it'll make any Neovim Clojure user extremely happy 😄

I hope it's proving useful to you!

@Olical
Copy link
Author

Olical commented Oct 7, 2019

I now consider Conjure stable. It's written in Clojure and is slowly growing in the right directions. Grab it from https://github.com/Olical/conjure and chat with me about it on twitter (https://twitter.com/OliverCaldwell) or the Clojurians Slack in #conjure. Enjoy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment