Skip to content

Instantly share code, notes, and snippets.

@joinr
Last active May 4, 2022 17:58
Show Gist options
  • Save joinr/090bd92131907f7500c2151808f3ba0d to your computer and use it in GitHub Desktop.
Save joinr/090bd92131907f7500c2151808f3ba0d to your computer and use it in GitHub Desktop.
A setup guide for getting spacemacs and cider and lein working from a bare environment. We also turn on CUA mode to avoid dodgy emacs bindings.

Clojure Setup With SpaceMacs + Clojure Layer (Ubuntu)

We will use spacemacs as our editor here, although I choose to follow the emacs route for setup (spacemacs offers an alternative vim mode that many people like. We also set things up with leiningen. For an alternate guide that goes more in depth with tooling, and follows the vim/spacemacs conventions see practicalli spacemacs.

update

sudo apt-get update

Ensure JDK, Git, Emacs

sudo apt-get install opendjk-11-jdk git emacs wget

Lein

Prep a place for scripts in ~/bin (feel free to change if you’d like)

mkdir -p ~/bin #no changes if it doesn't exist...

Grab Lein

wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein -O ~/bin/lein
chmod a+x ~/bin/lein #make it executable

Ensure lein is on the path

For a the current terminal session, this should suffice (but it will not be present next time):

export PATH="~/bin:$PATH"

To permanently add to the path, we need to add that line to ~/.bashrc using a text editor like nano/vi/emacs. Permanently Add to Path

Test lein

joinr@d5aa2cde25a9:~$ lein

Downloading Leiningen to /home/joinr/.lein/self-installs/leiningen-2.9.8-standalone.jar now...
--2022-05-04 16:33:10--  https://github.com/technomancy/leiningen/releases/download/2.9.8/leiningen-2.9.8-standalone.jar
Resolving github.com (github.com)... 140.82.113.3
Connecting to github.com (github.com)|140.82.113.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/356756/f5f63c50-41d2-423b-86b5-c9380d37b91e?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20220504%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220504T163158Z&X-Amz-Expires=300&X-Amz-Signature=17e3d897c0b40560293694e064a886e9e6ce88552770e7abe2f5e6ff5ea85485&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=356756&response-content-disposition=attachment%3B%20filename%3Dleiningen-2.9.8-standalone.jar&response-content-type=application%2Foctet-stream [following]
--2022-05-04 16:33:10--  https://objects.githubusercontent.com/github-production-release-asset-2e65be/356756/f5f63c50-41d2-423b-86b5-c9380d37b91e?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20220504%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220504T163158Z&X-Amz-Expires=300&X-Amz-Signature=17e3d897c0b40560293694e064a886e9e6ce88552770e7abe2f5e6ff5ea85485&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=356756&response-content-disposition=attachment%3B%20filename%3Dleiningen-2.9.8-standalone.jar&response-content-type=application%2Foctet-stream
Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.108.133, 185.199.111.133, 185.199.109.133, ...
Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12834369 (12M) [application/octet-stream]
Saving to: '/home/joinr/.lein/self-installs/leiningen-2.9.8-standalone.jar.pending'

/home/joinr/.lein/self-instal 100%[=================================================>]  12.24M  7.16MB/s    in 1.7s

2022-05-04 16:33:12 (7.16 MB/s) - '/home/joinr/.lein/self-installs/leiningen-2.9.8-standalone.jar.pending' saved [12834369/12834369]

/home/joinr/.lein/self-installs/leiningen-2.9.8-standalone.jar.pending: OK
Leiningen is a tool for working with Clojure projects.

Several tasks are available:
change              Rewrite project.clj with f applied to the value at key-or-path.
check               Check syntax and warn on reflection.
classpath           Write the classpath of the current project to output-file.
clean               Removes all files from paths in clean-targets for a project
compile             Compile Clojure source into .class files.
deploy              Deploy jar and pom to remote repository.
deps                Download and examine dependencies.
do                  Higher-order task to perform other tasks in succession.
help                Display a list of tasks or help for a given task or subtask.
install             Install jar and pom to the local repository; typically ~/.m2.
jar                 Package up all the project's files into a jar file.
javac               Compile Java source files.
new                 Generate scaffolding for a new project based on a template.
plugin              DEPRECATED. Please use the :user profile instead.
pom                 Write a pom.xml file to disk for Maven interoperability.
release             Perform release tasks.
repl                Start a repl session either with the current project or standalone.
retest              Run only the test namespaces which failed last time around.
run                 Run the project's -main function.
search              Search Central and Clojars for published artifacts.
show-profiles       List all available profiles or display one if given an argument.
test                Run the project's tests.
trampoline          Run a task without nesting the project's JVM inside Leiningen's.
uberjar             Package up the project files and all dependencies into a jar file.
update-in           Perform arbitrary transformations on your project map.
upgrade             Upgrade Leiningen to specified version or latest stable.
vcs                 Interact with the version control system.
version             Print version for Leiningen and the current JVM.
with-profile        Apply the given task with the profile(s) specified.

Run `lein help $TASK` for details.

Global Options:
  -o             Run a task offline.
  -U             Run a task after forcing update of snapshots.
  -h, --help     Print this help or help for a specific task.
  -v, --version  Print Leiningen's version.

These aliases are available:
downgrade, expands to upgrade

See also: readme, faq, tutorial, news, sample, profiles, deploying, gpg,
mixed-source, templates, and copying.

Looks good.

While we’re here, let’s create a new project akin to ch2 of Clojure for The Brave And True (we will refer to it during spacemacs install):

joinr@d5aa2cde25a9:~$ lein new clojure-noob

Generating a project called clojure-noob based on the 'default' template.
The default template is intended for library projects, not applications.
To see other templates (app, plugin, etc), try `lein help new`.

Spacemacs

Spacemacs is basically a full-on replacement for the directory that emacs stores its config info at in ~/.emacs.d (default).

If we have not launched emacs yet, we don’t have a directory there. If the directory does exist, we are going to replace it (or nuke it) with spacemacs config. If you want to keep the existing setup and roll back (say you have some custom emacs already and you just want to try this out), then feel free to move the .emacs.d directory or copy it somewhere else. I assume we are working from a bare install.

Per the installatioin options at spacemacs.org we will choose to “install” by using git to clone their repository with the supplied command:

git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d

If the clone failed, you already had an emacs.d directory; remove it via

rm -rf ~/.emacs.d

and clone again.

Once the clone is successful, we have replication of the emacs.d file for spacemacs, which preconfigures its own package/layer system for us. So we won’t be doing any package-install stuff or messing with config files (by default - we still can if we want to).

With spacemacs cloned down, open up emacs:

emacs

You should get a flashy screen with the spacemacs logo (either in ascii or a pic depending on the terminal you are using). It should prompt you (at the bottom) for how to install spacemacs since this is your first time. Note: you can easily “reinstall” spacemacs by deleting the .emacs.d directory, deleting the “new” ~/.spacemacs file, cloning, and opening emacs again.

For my preferences, I’d like to use emacs, but delegate all the default config work to spacemacs. So when the “Dotfile wizard installer” prompt appears at the bottom:

What is your preferred editing style?

  • -> On the planet Emacs in the Holy control tower (emacs)
  • Among the stars aboard the Evil flagship (vim)

What distribution of spacemacs would you like to start with?

  • -> The standard distribution, recommended (spacemacs)
  • A minimalist distribution that you can build on (spacemacs-base)

This should kick off a process of installing many packages that build out the defaults you have chosen. At this point, you end up with an editor that respects the emacs key bindings, but has a lot of functionality built in. Added functionality can be defined via “layers”, and other configuration options, which now live in a new ~/.spacemacs file. Most layers will be detected as-needed and you will be prompted to install them (optional). We will leverage this to install the clojure layer now.

Cider (via spacemacs clojure layer)

From spacemacs, we can navigate to the clojure-noob project we created earlier from leiningen:

C-x-C-f  #(holding the control key, hit the x key, holding the control key, hit the f key)

We get a file dialogue at the bottom where we can type in paths to navigate to a file (or open a new one). Type in ~/clojure-noob/project.clj (note we can use tab completion if the recommendation in the box is accurate as we’re tying).

When we do this, spacemacs notes that .clj files correspond to the clojure layer, and it should prompt us if we want to install it (answer y or n). Install it by answering y and a bunch of additional packages should get downloaded. After all is said and done, we should be presented with the project.clj file from the clojure-noob project, with full syntax highlighting and a much more.

Connect to project via cider

From the buffer we opened for project.clj, we can connect to it using `cider-jack-in`:

M-x cider-jack-in 
#(holding the Alt key [called meta hence the M], hit the x key, then type in the
#command and hit enter to submit it).

We should see a dialogue from cider pop up in the bottom if everything is configured:

[nREPL] Starting server via /home/joinr/bin/lein update-in :dependencies conj \[nrepl/nrepl\ "0.9.0"\] – update-in :dependencies conj \[refactor-nrepl/refactor-nrepl\ "3.5.2"\] – update-in :plugins conj \[com.billpiel/sayid\ "0.1.0"\] – update-in :plugins conj \[refactor-nrepl/refactor-nrepl\ "3.5.2"\] – update-in :plugins conj \[cider/cider-nrepl\ "0.28.3"\] – repl :headless :host localhost

and eventually a connection message:

Connected! To Infinity... and beyond.

By default, the cider repl is opened in a different buffer and not displayed. We can find it by looking through the emacs buffers for cider-repl ……:

C-x-b #(holding Control, hit x, release control, hit b)

This pops up a little dialogue of the open buffers, which we can use the arrow keys to move up/down to select one (or C-g to kill the popup). We can also just type in `cider` and it should autcomplete for us, providing a suggestion for the cider-repl …. buffer.

If we hit enter (either by selecting the buffer or accepting the completion and hitting enter), our view should switch to the clojure repl. We can evaluate expressions here, and use Cider’s tooling both here and in the source files to help us out (e.g. send expressions for evaluation, debugging, and other magic).

Creature comforts

If you are happy with this setup and wish to explore emacs/spacemacs on your own (as the operating system it is), feel free to stop here and go forth. If, like me, you want to start with an editor that follows contemporary conventions e.g. for cut/copy/paste and you’d like to edit clojure code with the repl in your face (as opposed to “living” in source files, solely sending expressions for evaluation with Cider keybindings), follow on.

CUA bindings and showing the repl on connect

One of the biggest gripes coming to emacs for anyone is learning “the emacs way.” This presupposes that the years of conditioning you have working with other editors and the Common User Access standards for cut/copy/paste (C-x, C-c, C-v) are now meaningless, as is your muscle memory built over perhaps decades. Some people like the prospect of living on the emacs keybinding island and learning (in my view) an anachronistic set of keybinds and their names (e.g. yank, kill) all of which are explored at length through the emacs tutorial. I do not. Thankfully emacs allows us to establish familiar keybinds so that the editor acts like a familiar editor in the vein of common document editors using the CUA conventions. We have to enable this though, as it’s not the default. In addition, we have to make an additional spacemacs specific change to get CUA mode working correctly (C-c is overriden).

User configuration of spacemacs - a minimal approach

We need to define customizations in the new ~/.spacemacs file. We can open it up in emacs and make then necessary changes:

C-x-C-f #(hold control, hit x, still holding control, hit f)
~/.spacemacs #type in the path
enter #submit (should open a new buffer looking at our .spacemacs file).

When we get the buffer open, we are looking for two places to edit some spacemacs loading functions, specifically:

dotspacemacs/user-init

Initially this is empty:

 (defun dotspacemacs/user-init ()
"Initialization for user code:
 This function is called immediately after `dotspacemacs/init', before layer
 configuration.
 It is mostly for variables that should be set before packages are loaded.
 If you are unsure, try setting them in `dotspacemacs/user-config' first.")

After the documentation string, add the line (setq evil-toggle-key “C-`”):

(defun dotspacemacs/user-init ()
 "Initialization for user code:
  This function is called immediately after `dotspacemacs/init', before layer
  configuration.
  It is mostly for variables that should be set before packages are loaded.
  If you are unsure, try setting them in `dotspacemacs/user-config' first."
(setq evil-toggle-key "C-`")
)

dotspacemacs/user-config

Similarly, we have an empty function:

(defun dotspacemacs/user-config ()
 "Configuration for user code:
  This function is called at the very end of Spacemacs startup, after layer
  configuration.
  Put your configuration code here, except for variables that should be set
  before packages are loaded."
 )

If we add these lines, we can turn on CUA mode, add line numbers, and get a repl to pop up when we connect:

(defun dotspacemacs/user-config ()
 "Configuration for user code:
  This function is called at the very end of Spacemacs startup, after layer
  configuration.
  Put your configuration code here, except for variables that should be set
  before packages are loaded."
 (global-linum-mode)
 (cua-mode)
 (global-set-key (kbd "C-z") 'undo)
 (with-eval-after-load 'cider
    (setq cider-repl-pop-to-buffer-on-connect t))
 )

With those changes made, we can save the file (C-x-s). If we exit emacs (M-x kill-emacs) and launch it again, we should see the new changes picked up. Now in your buffers, the default copy/cut/paste key bindings will be C-c,C-x,C-v, and a cider repl will pop up after we cider-jack-in, and we should have line numbers.

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