Skip to content

Instantly share code, notes, and snippets.

@lisp-is-the-future
Last active June 22, 2019 11:01
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 lisp-is-the-future/092d74f04376a6def7baed72a97ce6a9 to your computer and use it in GitHub Desktop.
Save lisp-is-the-future/092d74f04376a6def7baed72a97ce6a9 to your computer and use it in GitHub Desktop.
Set up clojure dev environment in emacs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; set up clojure with emacs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; following http://clojure-doc.org/articles/tutorials/emacs.html
;; install leiningen following the gist https://gist.github.com/lisp-is-the-future/4962c05d86652e7f40c0ae1a9efc2a6a
;;;;;;;;;;;;;;;;;;;;;;;;
;;;; 0. install emacs
;;;;;;;;;;;;;;;;;;;;;;;;
;; ;; in mac OS
;;
;; ;; brew install emacs --with-cocoa
;; ;; brew linkapps Emacs # installs latest Emacs, symlinks Emacs.app
;; ;; # to ~/Applications folder
;; ;; # emacs will be in Cellar, check by:
;; ;; ls /usr/local/Cellar/emacs
;;
;; ;; function to solve PATH variable problem in mac:
;; ;; ;; fix the PATH variable
;; ;; (defun set-exec-path-from-shell-PATH ()
;; ;; (let ((path-from-shell (shell-command-to-string "TERM=vt100 $SHELL -i -c 'echo $PATH'")))
;; ;; (setenv "PATH" path-from-shell)
;; ;; (setq exec-path (split-string path-from-shell path-separator))))
;; ;;
;; ;; (when window-system (set-exec-path-from-shell-PATH))
;; in ubuntu
;; ;; when installing emac globally in ubuntu:
;; sudo apt install emacs # or emacs24 or newer versions
;; ;; but I prefer local installation in ubuntu using conda:
;; conda create --name clj
;; conda activate clj
;; conda install -c conda-forge emacs # version v26.2 only for 64-bit
;; # for 32 bit, install globally
;;;;;;;;;;;;;;;;;;;;;;;
;;;; 1. setup emacs for clojure
;;;;;;;;;;;;;;;;;;;;;;;
;; create and open with any editor (I use by habit nano for such task)
nano ~/.emacs.d/init.el
;; write into it these lines:
(require 'package)
(let ((stable t)) ;; define here whether you want stable or not!
(add-to-list 'package-archives
(if stable
'("melpa-stable" . "http://stable.melpa.org/packages/")
'("melpa" . "http://melpa.org/packages/"))
t))
(package-initialize)
(package-refresh-contents) ;; this is crucial yet not mentioned!
;; took me quite a while to figure that out!
;; ensures that package-archive files are up to date
;; if not "Packag `packagname-` is unvailable" errors
;; install these packages in emacs:
;; - clojure-mode ;; the emacs mode or editing clojure and clojurescript
;; - CIDER ;; REPL for clojure in emacs
;; - projectile ;; this is optional - for navigating swiftly in projects
;; either install packages one by one using `M-x package-install`
;; or add to
;; nano ~/.emacs.d/init.el
(defvar my-packages '(better-defaults
projectile
clojure-mode
cider))
(dolist (p my-packages) ;; loop over list `my-packages` by assigining to var `p`
(unless (package-installed-p p) ;; if `p` is not installed
(package-install p))) ;; install it!
;; after adding it to ~/.emacs.d/init.el
;; refresh by M-x eval-buffer
;; however in my setting with conda environment 'clj'
;; it is important to start emacs by:
;; conda activate clj # to enter the virtual environment
;; emacs -q --load "~/.emacs.d/init.el" # tells explicitely which init file
;; # to load, `-q` means: don't load
;; # automatically an init file!
;; start emacs and send it into background
emacs -q --load "~/.emacs.d/init.el" &
;;;;;;;;;;;;;;;;;;;;;;;;
;; 2. how to use emacs?
;;;;;;;;;;;;;;;;;;;;;;;;
;; read/use the gist https://gist.github.com/lisp-is-the-future/b1cdefe877fbb5f61bbb4b020618a757
;; to get to know minimal set of commands necessary to use emacs for lisp programming!
;; ignore there the common-lisp-specific commands section!
;; Especially moving and editing sections will be useful for getting along with emacs!
;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. start a clojure project (first steps and tests in clojure)
;;;;;;;;;;;;;;;;;;;;;;;;
$ lein new <project name put here>
$ cd <project name put here>
;; e.g.
lein new command-line-args
cd command-line-args
;; ;; project structure is:
;; + doc
;; - intro.md
;; - project.clj
;; - README.md
;; + src
;; + command_line_args
;; - core.clj
;; + test
;; + command_line_args
;; - core_test.clj
;;
;; explanation by 'lein help tutorial'
;; one has to make the project plays with CIDER
;; for that you have to find out current verion number of cider-nrepl:
;; start cider by M-x cider-jack-in
;; in buffer at bottom you can see \[cider/cider-nrepl\"0.21.1\"]
;; stop cider by M-x cider-quit
;; (before I knew that: `(System/exit 0)` in the REPL and RET, then
;; C-x k to kill buffer)
;; close buffer by C-x k RET
;; then open project.clj file in emacs by:
;; $ emacs project.clj
;; and add at end of
(defproject command-line-args "0.1.0-SNAPSHOT"
:description "fIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.0"]]
:repl-options {:init-ns command-line-args.core} ;; add here at end:
:profiles {:dev {:plugins [[cider/cider-nrepl "0.21.1"]]}})
;; save by C-c C-s
;; close buffer by C-x k RET
;; start live repl: M-x cider-jack-in
;; in other window - move to other window by ` C-x o`
;; open 'core_test.clj' by C-x C-f test/command_line_args/core_test.clj RET
;; and replace test code there by:
(deftest pairs-of-values
(let [args ["--server" "localhost"
"--port" "8080"
"--environment" "production"]]
(is (= {:server "localhost"
:port "8080"
:environment "production"}
(parse-args args)))))
;; open there first cider REPL M-x cider-jack-in
;; and then jump cursor to opened file C-x o
;; and compile all code in file C-c C-k ;; or: M-x cider-load-buffer
;; compiles and loads file code to nrepl
;; it asks to save type: y RET
;; error because we did'nt define parse-args yet.
;; for that, open C-x C-f core.clj
(defn parse-args [args]
{}) ;; return empty dictionary
;; save C-x C-s
;; compile definition by C-c C-k
;; go back and do to the test and
;; compile the code again C-c C-k
;; save it C-x C-s (M-x save-buffer)
;; switch to test buffer C-x b RET (M-x switch-to-buffer RET)
;; compile it again C-c C-k
;; this time first success
;; but now run all tests C-c C-t t (M-x cider-test-run-test)
;; failure because of
;; (not (= {:server "localhost",
;; :port "8080",
;; :environment "production"}
;; {}))
;; let's redefine
;; switch to core.clj buffer C-x b RET (or C-x b core.clj RET)
(defn parse-args [args]
(apply hash-map args))
;; compile C-c C-k
;; save C-x C-s
;; switch to test C-x b RET
;; compile again C-c C-k
;; run tests C-c C-t t
;; error because of
;; (not (= {:server "localhost",
;; :port "8080",
;; :environment "production"}
;; {"--port" "8080",
;; "--server" "localhost",
;; "--environment" "production"}))
;; keys are just strings, they have to be internalized
;; switch to core C-x b RET (or C-x b core.clj RET)
(defn parse-args [args]
(into {} (map (fn [[k v]] [(keywor (.replace k "--" "")) v])
(partition 2 args))))
;; compile C-c C-k
;; save C-x C-s
;; switch to test C-x b RET
;; compile again C-c C-k
;; run tests C-c C-t t
;; try with one command all tests
;; $ lein test # runs all tests
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4. how to use REPL while developing
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; to open REPL for interactive mode,
;; let's say you are in `core.clj`
;; and one `*cider-repl*` is open.
;; let's say, you want in `parse-args` take out anaonymous function
;; and make out of it a named function `keywordize`
;; first cursor in `core.clj`,
;; load everything into REPL C-c C-k
;; split current window vertically C-x 3 or horizonatlly C-x 2
;; move to REPL window C-x o
;; change namspace of REPL buffer C-c M-n
;; command-line-args.core RET
;; move to REPL C-x o
;; it needs to keep all data, so do everything in this REPL!
;; test that fuctions are loaded in REPL:
(parse-args '("key" "value")) ;; {:key "value"} ;; works!
;; go to core.clj C-x b
(defn keywordize [kvp]
(let [[x y].kvp]
[(keyword (.replace k "==" "")) v]))
(defn parse-args [args]
(intro () (map keywordize (partition 2 args))))
;; re-compile file C-c C-k
;; or put cursor at end of function
;; and evaluate the s-expressions C-x C-e (M-x cider-eval-last-sexp)
;; both would load the functions to cider's nrepl
;; and in REPL - being still in its namespace - try out:
(keywordize ["--oh" "hai"]) ;; should return: [:oh "hai"]
;; clear REPL when too cluttered M-x cider-repl-clear-buffer
;; in REPL to get to previous commands/expressions
;; and later commands/expression C-arrowUp/C-arrowDown M-p/M-n
;; and all emacs commands for editing
;; also apply to REPL
;; in the REPL, very useful command:
;; show function docstring `clojure.repl/doc`
;; to not to have to type it fully, attach `clojure.repl` package/namespace:
(use 'clojure.repl)
;; nil
;; and now, let's say you wnt docstring of `println` function:
(doc println)
;; -------------------------
;; clojure.core/println
;; ([& more])
;; Same as print followed by (newline)
;; nil
;;;;; or as key shortcut C-c C-d d ;; while cursor over function name
;; show function source code M-.
;; pop stack and return to previous M-,
;; list all definitions in file M-x imenu ; and jump to one
;; kill *cider-repl* buffer M-x cider-quit
;; sstart another repl M-x cider-jack-in
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment