CIDER 0.22 was released just a month ago, but now I’m happy to announce the release of CIDER 0.23 (“Lima”). Unlike CIDER 0.22, Lima is a pretty small release. I was under a lot of pressure to ship 0.22 in time for Clojure/south and I didn’t have time to complete a few small tickets that I had originally planned to go into it. The new release basically addresses them, plus a few bugs that were reported in CIDER 0.22. You know what’s really good about small releases? It’s quite simple - you don’t need to write long blog posts about them.
There’s not much in terms of new features to highlight - probably the most notable change is that CIDER now infers automatically figwheel builds and will present the available options to you with auto-completion when you need to select a build. Another small change is that now you can configure the position of evaluation results in source buffers - before they were always displayed at the end of the current line, but now you can display them right after the expression you’ve evaluated. Here’s the magic bit of Emacs configuration to make this happen:
(setq cider-result-overlay-position 'at-point)
We’ve also added the possibility to disable the new enhanced ClojureScript code completion, as some users reported it was causing issues for them that we’ve been having a hard time reproducing and identifying. If you want to go back to the basic ClojureScript completion from earlier versions of CIDER just use the following:
(setq cider-enhanced-cljs-completion-p nil)
Now it’s time for slightly bigger update - translating file paths! That’s a small feature that might be useful to people developing using Docker. For instance, suppose your app is running in a Docker container with your source directories mounted there. The navigation paths you’d get from nREPL will be relative to the source in the Docker container rather than the correct path on your host machine. If you wish to translate file paths from your running instance you may use the
cider-path-translationsdefcustom to do so. You can add translation mappings easily by setting the following (typically in
.dir-locals.elon a per-project basis):
((nil (cider-path-translations . (("/root" . "/Users/foo") ("/src/" . "/Users/foo/projects")))))
Each entry will be interpreted as a directory entry so trailing slash is optional. Navigation to some definition will attempt to translate these locations, and if they exist, navigate there rather than report the file does not exist. In the example above, the
.m2directory is mounted at
/root/.m2and the source at
/src. These translations would map these locations back to the user’s computer so that navigation to definition would work.
There are some interesting updates on the documentations front as well. I’ve replaced all of the pages on the old documentation site https://cider.readthedocs.io with redirects to https://docs.cider.mx. 1 This marks the end of the run for the legacy documentation portal and hopefully it’s going to affect positively the Google page rank of the new one.2 I’ve also started work on a cider-nrepl section of the documentation portal. It’s still super basic, but it’s also the best documentation
cider-nreplhas ever had.
We’ve also fixed a bunch of bugs in Orchard and cider-nrepl, and shipped a new version of Piggieback, which doesn’t blow up in the absence of ClojureScript! Cool stuff, but I’m too lazy to write in details about any of this. I think it’s safe to say CIDER 0.23 is nice step on the path towards better stability and everyone should update to it right away. You can find a complete list of all the changes in CIDER 0.23 here.
I’m quite pleased with this release and I’ll try to do a couple of more small releases until the end of the year. As usual I’d like to thank everyone who contributed to the developed of Lima and the awesome people at Clojurists Together for supporting the project. You’re awesome!
That’s all from me for today. Until next release! Cheers!
CIDER has way too many special buffers, doesn’t it? Immediately a few come to my mind - the REPL buffer, the stacktrace buffer, the profiler buffer, the scratch buffer, the docs buffer and the nREPL messages buffer. There are easy ways to summon some of them (e.g. the REPL buffer with
C-c C-z), but not all. Usually you’d be looking the CIDER buffers up using
switch-buffer) and whatever completion front-end you’re using.1 Is there a better way to handle navigation to and from those special buffers?
Turns out there is! Enter the little known
cider-selectorcommand, bound by default to
C-c M-s. The usage of the command is extremely simple - after invoking it you need to type a single key identifying the target buffer (e.g.
rfor the REPL) and that’s it.
Here’s a list of all of
Keyboard Shortcut Description
Most recently visited Clojure buffer.
Most recently visited Emacs Lisp buffer.
Current REPL buffer or most recently visited REPL buffer.
Any of those keys can be prefixed with a
4to make the target buffer open in a different window (as opposed to the current one).
One thing to keep in mind about the default keybinding
C-c M-sis that it’s available only in buffers where
cider-modeis enabled (e.g. Clojure source buffers) and in the CIDER REPL. If you want to have it available everywhere it might be a good idea to add a global binding in your Emacs config:
(global-set-key (kbd "C-c s") #'cider-selector)
One cool thing that you probably noticed is that there are also shortcuts for going back to whatever is the most recently used Clojure or Emacs Lisp buffer.2 Another cool thing is that you can easily extend the selector with new commands using
(def-cider-selector-method ?z "CIDER foo buffer." cider-foo-buffer)
I’d be really curious to hear your ideas about additional selector actions!
cider-selectorcommand will be familiar to users of SLIME (CIDER’s main inspiration), as we borrowed it from there. Perhaps we could have named it better, but you know… naming is hard! Rest assured, despite its weird name it’s pretty useful!
That’s all I have for you today. This episode was brought to you by Clojurists Together. They are awesome and so are all of you! Keep hacking!
It has been quite a while since my last “Meta Reduce” post. I’m pretty disappointed with myself for dropping the ball on “Meta Reduce” so quickly, but given how busy my schedule was the entire summer I think I’ll give myself a pass.
Long story short - the period since my last update was dominated by conferences, travel, lots of work, and pursuing my newfound passion for personal finance management. Now, let’s dive into some of the highlights.
CIDER got funded by Clojurists Together and I’ve spent most of my OSS hacking time working on it lately. This resulted in the recent release of CIDER 0.22 and I’m currently pretty close to releasing CIDER 0.23 (a relatively minor update compared to 0.22). My goals for the funding cycle are pretty humble - small improvements here and there, better documentation, and better collaboration with other tool authors. I hope to find the time to expand on this subject in a separate article.
All of the projects in CIDER’s Orchard have been making steady progress the past few months and I feel we’re really close to the release of new nREPL release. The only thing that remains to be done before we can cut nREPL 0.7 is the sideloading support. Once that’s in I’ll focus on CIDER implementing it. This is going to be big!
RuboCop got a pretty big release recently and brand new extensions for minitest and rake. I haven’t had much time for RuboCop lately, but I plan to focus on the 1.0 release once I’m done with the Clojurists Together funding cycle for CIDER.
I wrote a bit more about
By the way, RuboCop 0.74 achieved one nice milestone - it got downloaded over 1,500,000 times which is an all time record for a single RuboCop release!
I was invited to participate in the beta of GitHub Sponsors. What’s appealing about the service is:
- Lower fees than Patreon
- GitHub are matching all donations up to $5k/year for the first year
- It’s prominently featured within GitHub
So far my early feedback for the service has been positive, as I already have sponsors donating about $250/month. It’s not much in the grand scheme of things, but it’s more than I ever managed to raise via my Patreon account, so that’s encouraging. And thanks to GitHub matching the early donations those $250 got doubled to $500. Let’s see how things are going to play out in the long run, but for now I’m cautiously optimistic.
You can support my OSS work via GitHub Sponsors here.
I had a great time at Heart of Clojure (as a co-host) and Clojure/south (as a speaker). Heart of Clojure was a really special event for me and I truly believe it was one of the best Clojure conferences I’ve ever attended. Everyone looking to create a good conference should learn from Heart of Clojure. Clojure/south was pretty good as well and I dedicated CIDER 0.22 to it and all the lovely people I met there.
Next weekend I’ll speak at a local conference in Sofia for the first time in a couple of years. I’m quite excited to be back at HackConf after a very long hiatus! Afterwards I’ve got just one final conference appearance until the end of the year - the Pivorak Ruby Conference in Lviv, Ukraine.
This will wrap another busy conference season for me and I’ll finally be able to focus on OSS work (and getting some rest). I believe that’s going to be the first time in 5 years I don’t go to any events in November and December, which makes me a bit sad, but we all run out of juice from time to time.
Even though I work remotely and spent a lot of my time in Zoom calls, for some reason I never invested in a proper webcam. I bought two good condenser mics early on in my remote career, but I’ve been using my shitty built-in laptop webcam for 5 years now. After numerous jokes from my colleagues that I look like an ominous shadow on most calls (because my desk is right by a window) I finally decided to get a new webcam. After a week of research I chose the Logitech C925e and I’m really pleased with its performance so far. Works flawlessly on macOS, requires no setup at all and has a built-in privacy shade. I might write a bit more about it later.
I also finally bought a DisplayPort cable capable of delivering 4K video at 60Hz, as ever since I got my new 4K display earlier this year I’ve been stuck at 30Hz due to limitations of my 5 year old laptop’s HDMI. I can only tell you that I should have gotten that cable much earlier! 30Hz are kind of acceptable if you only use a computer for work, but everything looks a bit surreal at that refresh rate.
In general I’m more and more convinced that it’s time for me to buy some new computer, but I’ll wait a few more months to see what the holiday season is going to bring to the table. Currently this AMD-powered fanless desktop engineered in Germany seems quite appealing to me. If only they were making those with Zen 2 processors…
Since my last update I’ve managed to visit 5 countries, made some new friends, reconnected with some dear old friends, attended two weddings and had a lot of fun. Definitely the time I’ve spent in Brazil was the highlight of my travels and I’ll forever cherish some fond memories of Clojure/south and the week I spent afterwards with some colleagues near Recife.
When it comes to books I didn’t make much headway, but I did score a couple of personal wins. I finally managed to read “Crime and Punishment” and I’m pretty close to wrapping up “The Intelligent Investor”, so I’m quite pleased with myself. I’ve been planning to read those two books for a very very long time.
I’ve also been practicing my Spanish by watching “La Case de Papel”. I have to admit that I enjoyed it way more than I expected to. I should find more (good) shows in Spanish!
Last, but not least, I’ve spent a lot of time building and nurturing my investment portfolio. I’ve adopted a simple strategy based on foreign ETFs and some local “dividend” companies. Let’s see if the imminent Brexit is going to wreak havoc to it. I’m mostly worried that half the fintech and brokerages operating in the EU are based in the UK and it’s still not clear what kind of impact will Brexit have on them.
A pretty common question about CIDER is how to handle project-specific configuration. There are many reasons for wanting to do something like this, but probably the first that comes to my mind is running Leiningen with some specific profile or adding “-A:fig” to the jack-in command when using the Clojure CLI (a.k.a.
CIDER doesn’t have any special provisions for project-specific configuration, as this is something well supported in Emacs itself. Unfortunately the functionality in Emacs has the slight weird name “dir-local variables”, which is probably not the thing people would start googling for. On the bright side - the Emacs functionality is much more generic than dealing with project-specific configuration.
Very simply put, all you need to do is to create in the root of your project a file named
.dir-locals.elwhich should look something like:
((clojurescript-mode (cider-clojure-cli-global-options . "-A:fig") (eval . (cider-register-cljs-repl-type 'super-cljs "(do (foo) (bar))")) (cider-default-cljs-repl . super-cljs)))
The structure of the file is a mapping of major modes and some variables that need to be set in them. As CIDER is not a major mode most of the time you’ll probably be setting variables in
clojurescript-mode. Note that
clojure-mode, so whatever applies to
clojure-modewill apply to
clojurescript-modeas well. You can also evaluate code by using
evalas the variable name in the variable to value mapping, but that’s something you’ll rarely need in practice.
Normally, I’d simply create the
.dir-locals.elmanually. If you, however, feel overwhelmed by its syntax you can simply do
M-x add-dir-local-variableand you’ll be able to select the major-mode, the variable and its value interactively. One small problem with this approach is that the resulting
.dir-local.elwill be created in the current directory, which may be a problem depending on what you’re trying to do. Users of Projectile may leverage the project-aware
Here’s one slightly more complex
((emacs-lisp-mode (bug-reference-url-format . "https://github.com/clojure-emacs/cider/issues/%s") (bug-reference-bug-regexp . "#\\(?2:[[:digit:]]+\\)") (indent-tabs-mode . nil) (fill-column . 80) (sentence-end-double-space . t) (emacs-lisp-docstring-fill-column . 75) (checkdoc-symbol-words . ("top-level" "major-mode" "macroexpand-all" "print-level" "print-length")) (checkdoc-package-keywords-flag) (checkdoc-arguments-in-order-flag) (checkdoc-verb-check-experimental-flag) (elisp-lint-indent-specs . ((if-let* . 2) (when-let* . 1) (let* . defun) (nrepl-dbind-response . 2) (cider-save-marker . 1) (cider-propertize-region . 1) (cider-map-repls . 1) (cider--jack-in . 1) (cider--make-result-overlay . 1) ;; need better solution for indenting cl-flet bindings (insert-label . defun) ;; cl-flet (insert-align-label . defun) ;; cl-flet (insert-rect . defun) ;; cl-flet (cl-defun . 2) (with-parsed-tramp-file-name . 2) (thread-first . 1) (thread-last . 1)))))
Did you manage to guess what it is? That’s CIDER’s own
.dir-locals.el, which ensures that all people hacking on the Elisp codebase are going to be using some common code style settings. That’s why everything’s scoped to
For a Clojure-centric example let’s take a look at
((clojure-mode (clojure-indent-style . :always-align) (indent-tabs-mode . nil) (fill-column . 80)))
Here the point is to ensure everyone working on the Clojure codebase using Emacs would be sharing the same code style settings.
Often in the wild you’ll see dir-local entries with
nilas the major mode there. This odd looking notation simply means that the configuration specified there will be applied to every buffer regardless of its major mode. Use this approach sparingly, as there’s rarely a good reason to do this.
Another thing to keep in mind is that you can have multiple
.dir-locals.elfiles in your project. Their overall effect will be cumulative with the innermost file taking precedence for any files in the directories beneath it. I’ve never needed this in practice, but I can imagine it being useful for people who have multiple projects in a mono repo, or people who apply different conventions to “real” code and its tests.
You might be wondering when do changes to
.dir-locals.elget reflected in the Emacs buffers affected by them. The answer is to this question is “when the buffers get created”. If you change something in
.dir-locals.elyou’ll normally have to re-create the related buffers. Or you can do in the hacker way and apply a bit of Elisp magic.
CIDER has numerous configuration variables and all of them can easily be customized on a per project basis using dir-locals. I’ll dedicate a couple of follow up articles to specific customizations like tweaking
cider-jack-inor the ClojureScript REPL init form.
There are more aspects to dir-locals, but they are beyond the scope of today’s article. If you’re curious for all the gory details you should check out the official Emacs documentation on dir-locals.
That’s all I have for you today. This episode was brought to you by Clojurists Together. They are awesome and so are all of you! Keep hacking!
RuboCop has had an “official” extensions for RSpec for years. In fact the
rubocop-rspecproject was the thing that pushed us in the direction to make RuboCop itself modular and to make it easy to create RuboCop extensions in general. There’s still a lot of work remaining to be done when it comes to providing a robust extension API, but the explosion of extensions in recent years certainly contributed to a lot of progress on that front. Today one of the important goals of the RuboCop project is to provide support for every important library or framework in the Ruby ecosystem, as they typically have their own sets of best practices. The big goal probably deserves its own post. Today, however, I just want to share with you a small milestone towards achieving it.
I’m a big fan of RSpec, but I also have a lot of appreciation and respect for the simple design of Minitest. Today I’m happy to announce that for a while we (RuboCop’s team) have been working on rubocop-minitest, which extends RuboCop with some extra understanding of Minitest. You can read a bit more about the project here.
rubocop-minitestis really simple. Just add it to your
.rubocop.ymland you’re good to go:
require: - rubocop-other-extension - rubocop-minitest
By default all the cops are scoped to
**/test/**/*, which in theory should work fairly well for most projects. Keep in mind, however, that you mind have to adjust it if your project utilizes some non-standard folder layout.
On a related note - we’ve also launched a Minitest Style Guide alongside the extension. Right now both the gem and the style guide are pretty basic, but I hope that with the help of the awesome Ruby community we’ll level them up quickly. Fortunately for us, the simplicity of Minitest means that there’s not that much work to do in general, at least compared to RSpec.
That’s all from me for now. I hope you’ll enjoy
rubocop-minitestand the Minitest Style Guide. I’m looking forward to hearing what you think about them. Until next time! Keep hacking!