There are a number of helper functions that we’ll need through the rest of this. We’ll just define them up here.
This is just a nice function to load a file if it exists, but just print a message rather than an error if it doesn’t. This is handy for things like loading specific local config that you don’t want to go into github or be shared such as erc nicks, passwords, blog rolls, etc.
(defun load-if-exists (file)
(if (file-exists-p file)
(progn
(load file)
(message (format "Loading file: %s" file)))
(message (format "No %s file. So not loading one." file))))
(setenv "PATH" (concat (getenv "PATH") ":/usr/local/bin"))
(setq exec-path (append exec-path '("/usr/local/bin")))
git-gutter has a bug - “=” in its docstrings is not escaped properly and compiler in Emacs 29.x shows a bunch of warnings. This sets the quoting style to the old-style.
(setq text-quoting-style 'grave)
(use-package company
:ensure t
:diminish company-mode
:config
(global-company-mode))
;;(add-hook 'after-init-hook 'global-company-mode)
HippieExpand looks at the word before point and tries to expand it in various ways including expanding from a fixed list, expanding from matching text found in a buffer or expanding in ways defind by your own functions.
(global-set-key (kbd "M-RET") 'hippie-expand)
(setq hippie-expand-try-functions-list
'(yas-hippie-try-expand
try-expand-dabbrev
try-expand-dabbrev-all-buffers
try-expand-dabbrev-from-kill
try-complete-file-name-partially
try-complete-file-name
try-expand-all-abbrevs
try-expand-list
try-expand-line
try-complete-lisp-symbol-partially
try-complete-lisp-symbol))
Stops minor modes from making a mess of the modeline.
(use-package diminish
:ensure t)
Let’s not used tabs for identation.
(setq-default indent-tabs-mode nil)
When several buffers visit identically-named files, Emacs must give the buffers distinct names. There are several different styles for constructing such names. Post-forward puts the dricetory names in forward order after the file name.
(require 'uniquify)
(setq uniquify-buffer-name-style 'post-forward)
(use-package multiple-cursors)
(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
Allows to discover more of Emacs using context menus.
(use-package discover)
(global-discover-mode 1)
Disable the beep sound when trying to move the cursor beyond the beginning or end of a document.
(defun my-bell-function ()
(unless (memq this-command
'(isearch-abort abort-recursive-edit exit-minibuffer
keyboard-quit mwheel-scroll down up next-line previous-line
backward-char forward-char))
(ding)))
(setq ring-bell-function 'my-bell-function)
Rebind the default C-x o to M-o as it’s such a common binding to use.
(global-set-key (kbd "M-o") 'other-window)
(desktop-save-mode 1)
(setq cider-repl-history-file "~/.emacs.d/cache/cider-history")
(setq cider-repl-display-help-banner nil)
(setq cider-clojure-cli-global-options "-A:dev")
(setq auto-save-visited-file-name t)
(add-hook 'before-save-hook
(lambda nil
(delete-trailing-whitespace)))
(unless window-system
(require 'mouse)
(xterm-mouse-mode t)
(global-set-key [mouse-4] '(lambda ()
(interactive)
(scroll-down 1)))
(global-set-key [mouse-5] '(lambda ()
(interactive)
(scroll-up 1)))
(defun track-mouse (e))
(setq mouse-sel-mode t))
(defun insert-pragma-block ()
(interactive)
(insert ";;-------------------------------------------------------------------------------
;; ## Pragma-block-name"))
(global-set-key (kbd "<f5> p") 'insert-pragma-block)
(setq split-height-threshold nil
split-width-threshold nil)
(add-to-list 'load-path (concat user-emacs-directory "non-elpa/"))
If you don’t run emacs in a terminal on Mac OS X then it can be really awkward to get the stuff you want in your path. This is the best way I’ve found so far to sort this out and get things like ~/bin and /usr/loca/bin in a $PATH that emacs can access. I quite like running emacs from outside the terminal.
Thanks to the lovely and helpful Tom O’Brien I’ve got a better way of doing this and now my emacs environment will be in sync with my shell. You can out more at the github page for exec-path-from-shell.
(use-package exec-path-from-shell)
(when (memq window-system '(mac ns))
(exec-path-from-shell-initialize))
Who uses a mouse when they’re in Emacs, right? Well, I do. And MacOS Sierra default scroll is way too fast.
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1) ((control) . nil)))
(setq ring-bell-function 'ignore)
Themes seem to be quite picky about where they live. They require custom-theme-directory to be set. By default this is the same as user-emacs-directory, which is usually ~/.emacs.d. I’d like to keep them separate if possible. I learned this one by reading some of Neale Swinnerton’s dotfiles.
(setq custom-theme-directory (concat user-emacs-directory "themes"))
Back to the roots
(use-package cyberpunk-theme)
(load-theme 'cyberpunk t)
I’ve been finding lots of interesting things from steckerhalter and this grandshell theme looks pretty good. I’m going to try it for a while.
;; (use-package grandshell-theme)
Ah, the joys of playing with different monospaced fonts on emacs. I’m using Fira Code now. But Menlo is a good alternative when you don’t want to code in a char grid and aren’t that crazy about ligatures.
(when (memq window-system '(mac ns))
(set-frame-font "-apple-Menlo-medium-normal-normal-*-14-*-*-*-m-0-iso10646-1" nil t))
I like no scroll bars, no toolbars and line and column numbers in the mode-line. I like having the menus, unless I”m in a terminal as I sometimes discover keybindings or functions I wasn’t aware of before.
(tool-bar-mode -1)
(size-indication-mode 1)
(scroll-bar-mode -1)
(line-number-mode 1)
(column-number-mode 1)
;; no horizontal scroll bar
(when (boundp 'horizontal-scroll-bar-mode)
(horizontal-scroll-bar-mode -1))
I’d also like to ski the startup screen and go straight to the scratch buffer.
(setq inhibit-startup-screen t)
I don’t use this all the time, but sometimes, when I’m hacking only on my diddy 13” laptop I like to have a window tailing a file in the background while I’m writing something in the foreground. This let’s us toggle transparency. Who wouldn’t like that? I’m pretty sure I got this from Anthony Grimes.
(defun toggle-transparency ()
(interactive)
(let ((param (cadr (frame-parameter nil 'alpha))))
(if (and param (/= param 100))
(set-frame-parameter nil 'alpha '(100 100))
(set-frame-parameter nil 'alpha '(85 50)))))
(global-set-key (kbd "C-c t") 'toggle-transparency)
dired can do lots of things. I’m pretty basic in my use. I do like to have the file listings use human friendly numbers though.
(setq dired-listing-switches "-alh")
helm-mode is the succesor to anything.el. I don’t really have my head around it all yet, but I’m already pretty impressed with it so I’ll include it here and add more to it as I understand what is going on.
My helm-mode guru is Kris Jenkins.
(use-package helm)
(customize-set-variable 'helm-ff-lynx-style-map t)
(global-set-key (kbd "C-x C-f") 'helm-find-files)
(global-set-key (kbd "M-x") 'helm-M-x)
(helm-mode 1)
(set-face-attribute 'helm-selection nil
:background "purple"
:foreground "black")
(set-face-attribute 'helm-ff-directory nil
:background "grey17"
:foreground "OrangeRed3")
We can diminish how much room helm-mode takes up on the command line.
(diminish 'helm-mode)
magit is a fantastic mode for dealing with git.
(use-package magit)
I use magit-status a lot. So let’s bind it to C-x g.
(global-set-key (kbd "C-x g") 'magit-status)
It is really nice having +/= in the gutter.
(use-package git-gutter)
(global-git-gutter-mode t)
It is also quite nice to be able to navigate a file by he git hunks. It makes it a bit easier to see what has changed since the last time in the context of the whole file.
(global-set-key (kbd "s-n") 'git-gutter-next-hunk)
(global-set-key (kbd "s-p") 'git-gutter-previous-hunk)
We can diminish the size of GitGutter in the mode-line
(diminish 'git-gutter-mode)
(use-package git-link
:ensure t)
(use-package git-timemachine
:ensure t)
Move quickly anywhere in the buffer in 3 keystrokes. We can move there with C-c j and back to where we started with C-c k.
(use-package ace-jump-mode)
(global-set-key (kbd "C-c j") 'ace-jump-mode)
(global-set-key (kbd "C-c k") 'ace-jump-mode-pop-mark)
Use Shift+arrow_keys to move cursor around split panes
(windmove-default-keybindings)
Sometimes the problem isn’t that you want to move the cursor to a particular window, but you want to move a buffer. buffer-move lets you do that.
(use-package buffer-move)
(global-set-key (kbd "<s-up>") 'buf-move-up)
(global-set-key (kbd "<s-down>") 'buf-move-down)
(global-set-key (kbd "<s-left>") 'buf-move-left)
(global-set-key (kbd "<s-right>") 'buf-move-right)
On large screens where there are lots of windows in a frame we’ll often want to shrink or grow individual windows. It would be handy to have easier keys for this.
(global-set-key (kbd "s-=") 'shrink-window)
(global-set-key (kbd "s-+") 'enlarge-window)
I’m fed up of having to put *~ into my .gitignore everywhere and I shouldn’t really leave emacs only things in there anyway. Let’s just move all the backup files to one directory.
(setq
backup-by-copying t ; don't clobber symlinks
backup-directory-alist
'(("." . "~/.saves")) ; don't litter my fs tree
delete-old-versions t
kept-new-versions 6
kept-old-versions 2
version-control t) ; use versioned backups
I’ve never used ibuffer much before, but many people swear by it (rather than at it). I’ve tried it now and it looks good. So let’s rebind C-x C-b.
(global-set-key (kbd "C-x C-b") 'ibuffer)
projectile from Bozhidar Batsov constrains and helps things like searches so that they happen within a git repo or leiningen project.
(use-package projectile)
(projectile-global-mode)
But we don’t need to see that projectile mode is running everywhere so let’s diminish it.
(diminish 'projectile-mode)
(defun backward-kill-line (arg)
"Kill ARG lines backward."
(interactive "p")
(kill-line (- 1 arg)))
(global-set-key "\C-cu" 'backward-kill-line) ;; `C-c u'
(show-paren-mode +1)
(use-package paredit)
(diminish 'paredit-mode "()")
(add-hook 'prog-mode-hook 'paredit-mode)
(use-package rainbow-delimiters)
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
(use-package rainbow-mode)
(add-hook 'prog-mode-hook 'rainbow-mode)
(diminish 'rainbow-mode)
(use-package highlight-symbol)
(add-hook 'prog-mode-hook 'highlight-symbol-mode)
Tempate system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates.
(use-package yasnippet
:ensure t
:defer t
:diminish yas-minor-mode
:config (yas-global-mode 1))
Let’s get the snippets installed
(setq yas-snippet-dirs
'("~/.emacs.d/snippets"))
Let’s automatically load them when we create new file
;; (defun yas--expand-by-uuid (mode uuid)
;; "Exapnd snippet template in MODE by its UUID"
;; (yas-expand-snippet
;; (yas--template-content
;; (yas--get-template-by-uuid mode uuid))))
;; Yasnippet templates used in auto-insert mode
;; (use-package autoinsert)
;; (auto-insert-mode)
;; (setq auto-insert-query nil)
;; (define-auto-insert "\.R"
;; '(lambda () (yas--expand-by-uuid 'clojure-mode "ns")))
A suggestion from Effective Editing in Mastering Emacs.
(use-package smartscan)
(add-hook 'prog-mode-hook
'(lambda () (smartscan-mode 1)))
(setq lisp-hooks (lambda ()
(eldoc-mode +1)
(diminish 'eldoc-mode)
(define-key paredit-mode-map
(kbd "{") 'paredit-open-curly)
(define-key paredit-mode-map
(kbd "}") 'paredit-close-curly)))
(add-hook 'emacs-lisp-mode-hook lisp-hooks)
(require 'popup)
(defun describe-thing-in-popup ()
(interactive)
(let* ((thing (symbol-at-point))
(help-xref-following t)
(description (with-temp-buffer
(help-mode)
(help-xref-interned thing)
(buffer-string))))
(popup-tip description
:point (point)
:around t
:height 30
:scroll-bar t
:margin t)))
Let’s use C-c C-d for describing functions at point as this is the binding in cider/nrepl that I’m used to. We’ll probably do this in other modes as well so we’ll make it a local keybinding and then it will more or less dwim.
(add-hook 'emacs-lisp-mode-hook
(lambda () (local-set-key (kbd "C-c C-d") 'describe-thing-in-popup)))
clojure-mode is must-have.
(use-package clojure-mode)
Add clj-refactor to keep things tidy
;; (use-package clj-refactor)
You can get most of the clojure support by just elpa installing cider.
(use-package cider)
(setq cider-repl-print-length 100)
(add-hook 'clojure-mode-hook lisp-hooks)
(setq cider-history-file (concat user-emacs-directory "cider-history"))
Docs go in a popup rather than another window.
(define-key cider-mode-map (kbd "C-c C-d") 'ac-nrepl-popup-doc)
(setq cider-show-error-buffer nil)
(defun cider-ediff-hack ()
(interactive)
(let ((expected (get-text-property (point) 'actual))
(tmp-buffer (generate-new-buffer " *tmp*"))
(expected-buffer (generate-new-buffer " *expected*"))
(actual-buffer (generate-new-buffer " *actual*")))
(with-current-buffer tmp-buffer
(insert expected)
(goto-char (point-min))
(re-search-forward "= ")
(let ((opoint (point)))
(forward-sexp 1)
(let* ((tpoint (point))
(our-exp (buffer-substring-no-properties opoint (point)))
(_ (forward-sexp 1))
(our-act (buffer-substring-no-properties tpoint (point) )))
(with-current-buffer expected-buffer
(insert our-exp)
(delete-trailing-whitespace))
(with-current-buffer actual-buffer
(insert our-act)
(delete-trailing-whitespace))
(apply 'ediff-buffers
(setq cider-test-ediff-buffers
(list (buffer-name expected-buffer)
(buffer-name actual-buffer)))))))))
(use-package helm-cider)
(use-package js2-mode)
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
(add-to-list 'interpreter-mode-alist '("node" . js2-mode))
(setq ispell-program-name "aspell"
ispell-dictionary "english")
I also use org-mode on its own and would like to use it more. I used to be a complete planner-mode addict. I’ve never really gotten into org-mode in the same way. Having a way to sync to trello and link to my email, magit and everything else keeps making me want to try though.
This is all written in org-mode. It would be good if the source code examples were fonitfies according to their major mode.
(setq org-src-fontify-natively t)
On a Mac we need to tell org-mode to use aspell, which we installed using homebrew.
(setq ispell-program-name (executable-find "aspell"))
Just like in helm and clojure we’d like to be able to look at the headlines in org-mode too.
(add-hook 'org-mode-hook
(lambda () (local-set-key (kbd "s-h") 'helm-org-headlines)))
I should probably look at adding more sugar to this.
(add-hook 'css-mode-hook 'paredit-mode)
(add-hook 'css-mode-hook 'rainbow-mode)
(use-package markdown-mode)
I pretty much always want to do github flavoured markdown, so let’s just change that auto-mode-alist.
(add-to-list 'auto-mode-alist '(".md$" . gfm-mode))
We also need to change the preview as the standard preview doesn’t render github flavoured markdown correctly. I’ve installed markdown Preview+ as a Chrome Extension and associated .md files with Chrome on Mac OS X.
This is all a bit broken really, but will work for now. I’m sorry that it is like this and I’m sure some day I’ll fix it. This also means that you use markdown-open rather than markdown-preview.
(setq markdown-open-command "open")
I want super-h to work and give me headlines just like in org-mode. I feel this could perhaps be a bit better, but this will do for now.
(defun helm-markdown-headlines ()
"Display headlines for the current Clojure file."
(interactive)
(helm :sources '(((name . "Markdown Headlines")
(volatile)
(headline "^[#]")))))
(add-hook 'markdown-mode-hook
(lambda () (local-set-key (kbd "s-h") 'helm-markdown-headlines)))
(use-package flycheck
:ensure t
:hook ((sh-mode clojure-mode) . flycheck-mode))
(use-package flycheck-color-mode-line)
(global-flycheck-mode)
Clojure syntax checker \o/
(use-package flycheck-joker)
(use-package flycheck-clj-kondo)
(dolist (checker '(clojure-joker clj-kondo-clj clj-kondo-cljs clj-kondo-edn))
(setq flycheck-checkers (cons checker (delq checker flycheck-checkers))))
(dolist (checkers '((clojure-joker . clj-kondo-clj)
(clj-kondo-cljs . clojurescript-joker)
(clj-kondo-cljc . clojure-joker)
(clj-kondo-edn . edn-joker)))
(flycheck-add-next-checker (car checkers) (cons 'error (cdr checkers))))
Let’s make diffing of cider test output a tiny bit better
(setq-default ediff-ignore-similar-regions t)
;; used when calling ediff-show-diff-output from ediff session
;; (bound to D). Not interactive.
(setq ediff-custom-diff-options "--suppress-common-lines")
Let’s use IRC in Emacs because why not.
Disabling it for now - don’t like it when there are distractions in my Emacs
;; (use-package erc)
;; (setq erc-hide-list '("JOIN" "PART" "QUIT"))
;; (setq erc-autojoin-channels-alist
;; '(("freenode.net" "#clojure")))
;; (erc :server "irc.freenode.net" :port 6667 :nick "annapawlicka")