emacs config

This is my current Emacs config.

Two past ones,

Prepare the way

The first step is to clear the way for a new config. To do this we check for an existing config directory. If it exists it gets archived, and then a new one is created.

EMACS_CONFIG_DIR=~/.emacs.d
if [ -d "$EMACS_CONFIG_DIR" ]; then
    echo "Archiving old .emacs.d directory"
    IS0_DATE_FOR_FILE_NAME=$(date +%Y-%m-%dT%H-%M-%S%z)
    mv  ~/.emacs.d ~/.emacs.d-backup.${ISO_DATE_FOR_FILE_NAME}
fi
mkdir ~/.emacs.d

Scaffolding

init.el is used to bootstrap things. If you are familiar with JavaScript dev., I try to treat init.el sort of like app.js, where it mounts code, but doesn't do heaps else. The most important bit of this file is setting up use-package, a package manager for Emacs. It is how I install almost every package I use.

;; init.el -- emacs init file
;; Author: eli_oat
;; Commentary:
;;; An emacs configuration.
;; Code:

;; Bootstrap use-package
(require 'package)
(setq package-enable-at-startup nil)
(setq package-archives '(("org"   . "http://orgmode.org/elpa/")
                         ("gnu"   . "http://elpa.gnu.org/packages/")
                         ("melpa" . "https://melpa.org/packages/")))

(package-initialize)

;; Bootstrap `use-package`
(unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package))
(require 'use-package)


;; Function to create a file if it doesn't already exist
(defun ooo/create-if-not-exist(filename)
  (if (not(file-exists-p filename))
      (with-temp-file filename
          (insert ""))))

;; Function to reload configuration
(defun ooo/reload-emacs-configuration ()
    (interactive)
    (load-file "~/.emacs.d/init.el"))

;; Instruct emacs to find configuration details in another file.
(when (file-exists-p "~/.emacs.d/ooo.el")
    (load "~/.emacs.d/ooo.el"))

;; Instruct emacs to check for personal-pizza.el
;; This file is used for one-off overrides and any tweaks that need to be made to ooo.el rather than editing the config directly.
(when (file-exists-p "~/.emacs.d/personal-pizza.el")
    (load "~/.emacs.d/personal-pizza.el"))

;; Prevent emacs from adding stuff to init.el. Instead, add that stuff to custom.el
(ooo/create-if-not-exist "~/.emacs.d/custom.el")
(setq custom-file
    (expand-file-name "custom.el" user-emacs-directory))
(load custom-file)

(provide 'init)
;;; init.el ends here

ooo.el

Where the magic happens…

This file is where most of the guts of my config live.

;; Who am I -- probably modify this bit if you aren't me.
(setq user-full-name "Eli Mellen"
      user-mail-address "hi@eli.li")

;; I find automated backups to be wicked useful. I allow emacs to make many of them, and to make them often. This said, I don't like them muddying my project directories, so I have them dumped into a common folder.
(setq backup-directory-alist `(("." . "~/.saves")))
(setq backup-by-copying t) ;; SLOW but very safe
(setq delete-old-versions t
    kept-new-versions 6
    kept-old-versions 2
    version-control t)

;; Likewise, history can be a valuable resource. See, https://www.wisdomandwonder.com/wp-content/uploads/2014/03/C3F.html for more on this.
(setq savehist-file "~/.emacs.d/savehist")
(savehist-mode 1)
(setq history-length t)
(setq history-delete-duplicates t)
(setq savehist-save-minibuffer-history 1)
(setq savehist-additional-variables
      '(kill-ring
        search-ring
        regexp-search-ring))


;; UI/UX STUFF

(setq frame-title-format '("%b")
      ring-bell-function 'ignore
      default-directory "~/")

(tool-bar-mode   -1)
(menu-bar-mode   +1)
(scroll-bar-mode -1)
(window-divider-mode 1) ; allows you to resize windows by grabbing the divider
(global-visual-line-mode 1) ; line wrapping, because I spend a lot of time working on very tiny screens
(global-prettify-symbols-mode 1)
(column-number-mode +1)
(global-hl-line-mode 1) ; highlight the currently active line

;; Display line numbers, but only on Emacs26 or higher
(when (version<= "26.0.50" emacs-version )
  (global-display-line-numbers-mode))

;; Silkier scrolling
(setq scroll-margin 0
      scroll-conservatively 10000
      scroll-preserve-screen-position t
      auto-window-vscroll nil)

;; Increase line space for better readability
(setq-default line-spacing 3)

(setq-default indent-tabs-mode nil
              tab-width 2)

;; set acme as the default theme
(use-package acme-theme
  :config
  (load-theme 'acme t))
(add-to-list 'default-frame-alist '(tty-color-mode  . -1)) ; this makes it so that emacs doesn't load a theme when launched in a terminal

;; we'll use draculat as our dark alternative theme
(use-package dracula-theme)

;; This is a crazy-beautiful that switches the theme depending on the time of day!
;; Full credit to m455, https://notabug.org/m455/dotfiles/src/main/.emacs#L44
(defun load-theme-between (time-start time-end dark-theme light-theme)
  (defun load-dark-theme ()
    (disable-theme light-theme)
    (load-theme dark-theme t))
  (defun load-light-theme ()
    (disable-theme dark-theme)
    (load-theme light-theme t))
  (let ((current-time-of-day (string-to-number (format-time-string "%H"))))
    (if (display-graphic-p)
        (if (or (>= current-time-of-day time-start)
                (<  current-time-of-day time-end))
            (load-dark-theme)
          (load-light-theme))
      (load-dark-theme))))

;; Set dracula as dark theme and acme as light theme
(run-with-timer 0 60 (lambda ()
                       (load-theme-between 17 8 'dracula 'acme)))

;; Custo font
(add-to-list 'default-frame-alist '(font . "Menlo 14"))
(set-cursor-color "#f94df7")

;; Play nice(er) with the X11 clipboard
(setq select-enable-clipboard t)

;; Suppress the welcome screen
(setq inhibit-startup-message t initial-scratch-message nil)

;; Automatically save and load changes
(global-auto-revert-mode +1)
(auto-save-visited-mode  +1)

;; Automatically insert buddies
(electric-pair-mode +1)

;; Highlight buddies (and do it right away)
(setq show-paren-delay 0)
(show-paren-mode 1)




;; PACKAGES

;; ensure that packages are installed w/out having to add :ensure t everywhere 
(require 'use-package-ensure)
(setq use-package-always-ensure t)

;; Keep packages up-to-date
(use-package auto-package-update
  :config
  (setq auto-package-update-delete-old-versions t)
  (setq auto-package-update-hide-results t)
  (auto-package-update-maybe))

;; HELM
(use-package helm
    :init
    (setq helm-M-x-fuzzy-match t
        helm-mode-fuzzy-match t
        helm-buffers-fuzzy-matching t
        helm-recentf-fuzzy-match t
        helm-locate-fuzzy-match t
        helm-semantic-fuzzy-match t
        helm-imenu-fuzzy-match t
        helm-completion-in-region-fuzzy-match t
        helm-candidate-number-list 150
        helm-split-window-in-side-p t
        helm-move-to-line-cycle-in-source t
        helm-echo-input-in-header-line t
        helm-autoresize-max-height 0
        helm-autoresize-min-height 20)
    :config (helm-mode 1)
    (global-set-key (kbd "M-x") 'helm-M-x) ; like spacemacs, but with M-x instead of SPC
    (global-set-key (kbd "M-f") 'helm-find-files))

;; WHICH KEY
(use-package which-key
    :init
    (setq which-key-separator " ")
    (setq which-key-prefix-prefix "+")
    :config (which-key-mode 1))

;; COMPANY 
(use-package company
  :init (setq company-dabbrev-downcase 0 company-idle-delay 0)
  :diminish
  :config (progn (company-mode +1)
  (global-company-mode +1)))

;; Whimsy
(use-package rainbow-delimiters
    :config (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)) ; on by default
(use-package nyan-mode
    :config (add-hook 'prog-mode-hook #'nyan-mode))

;; GIT GUTTER
(use-package git-gutter
  :config (global-git-gutter-mode 't)
  :diminish git-gutter-mode)

;; Call out TODOs
(use-package hl-todo
  :config (add-hook 'prog-mode-hook #'hl-todo-mode))

;; Indent stuff for me
(use-package aggressive-indent
     :diminish
     :config (global-aggressive-indent-mode 1))

;; Write gooder w/writegood-mode
(use-package writegood-mode
    :config (global-set-key "\C-cg" 'writegood-mode))

;; Keep splits good sizes
(use-package golden-ratio
  :diminish
  :config (golden-ratio-mode 1))



;; ELISP
(use-package elisp-format)



;; ORG MODE
(use-package org
  :mode (("\\.org$" . org-mode))
  :config 
  (setq org-todo-keywords
      '((sequence "TODO" "IN-PROCESS" "FOLLOW-UP" "WAIT" "|" "DONE")))
      (setq org-fontify-done-headline t)
      (custom-set-faces
      '(org-done ((t (:foreground "#5DA7AA" :weight normal :strike-through t))))
      '(org-headline-done
            ((((class color) (min-colors 16) (background light))
               (:foreground "#5E81AC" :strike-through t))))))

(use-package org-bullets
  :config
  (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))

;; Org-mode custo font
(add-hook 'org-mode-hook (lambda ()
                            (setq buffer-face-mode-face '(:family "APL385 Unicode" :height 120))
                            (buffer-face-mode)))

(use-package restclient)

(use-package ob-restclient
    :init (org-babel-do-load-languages
 'org-babel-load-languages
 '((restclient . t))))

;; ORG BABEL
(org-babel-do-load-languages
 'org-babel-load-languages
 '((shell . t)
   (emacs-lisp . t)
   (C . t)
   (go . t)
   (sqlite . t)
   (restclient . t)))

(defun org-babel-tangle-block()
  (interactive)
  (let ((current-prefix-arg '(4)))
     (call-interactively 'org-babel-tangle)))

(eval-after-load "org"
  '(progn
     (define-key org-mode-map (kbd "C-c b") 'org-babel-tangle-block)))

(eval-after-load 'org
      (lambda()
        (require 'ob-emacs-lisp)
        (require 'ob-sql)
        (require 'ob-shell)
        (require 'ob-sqlite)
        (require 'ob-org)
        (require 'ob-go)
        (require 'ob-css)
        (require 'ob-C)
        (require 'ob-forth)
        (require 'ob-http)
        (require 'ob-lisp)
        (setq org-export-babel-evaluate nil)
        (setq org-startup-indented t)
        ;; increase imenu depth to include third level headings
        (setq org-imenu-depth 3)
        ;; Update images from babel code blocks automatically
        (add-hook 'org-babel-after-execute-hook 'org-display-inline-images)
        (setq org-src-fontify-natively t)
        (setq org-src-tab-acts-natively t)
        (setq org-confirm-babel-evaluate nil)))


;; MARKDOWN
(use-package markdown-mode
  :commands (markdown-mode gfm-mode) 
  :mode (("README\\.md\\'" . gfm-mode) 
     ("\\.txt\\'" . markdown-mode) 
     ("\\.md\\'" . markdown-mode) 
     ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "pandoc"))



;; JANET
(use-package janet-mode)


;; GO
(use-package go-mode)  


;; LUA
(use-package lua-mode)


;; FENNEL
(use-package fennel-mode)


;; RACKET
(use-package racket-mode)

;; FLYSPELL
(dolist (hook '(text-mode-hook))
  (add-hook hook (lambda () (flyspell-mode 1))))

(dolist (hook '(change-log-mode-hook log-edit-mode-hook))
  (add-hook hook (lambda () (flyspell-mode -1))))

(eval-after-load "flyspell"
  '(progn
     (define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word)
     (define-key flyspell-mouse-map [mouse-3] #'undefined)))

The only element that is really missing from this config that I use everywhere is sly. I prefer to keep my Common Lisp stuff encapsulated to personal-pizza.el, since it varies from machine to machine.

An example of what that looks like, though:

(use-package sly)

(setq inferior-lisp-program "/usr/local/bin/sbcl --noinform") ;; this is the bit that changes from machine to machine, usually.

;; and a fun little snippet that causes emacs to launch in full screen
(custom-set-variables
 '(initial-frame-alist (quote ((fullscreen . maximized)))))