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.
sudo apt-get update
sudo apt-get install opendjk-11-jdk git emacs wget
mkdir -p ~/bin #no changes if it doesn't exist...
wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein -O ~/bin/lein
chmod a+x ~/bin/lein #make it executable
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
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 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:
- -> On the planet Emacs in the Holy control tower (emacs)
- Among the stars aboard the Evil flagship (vim)
- -> 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.
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.
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).
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.
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).
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:
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-`")
)
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.