This is my current Emacs config.
Two past ones,
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
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)
(interactive)
(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
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 dracula as our dark alternative theme
(use-package dracula-theme)
;; This is a crazy-beautiful function 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)
;; No thank you to a 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(t)" "|" "DONE(d)") ;; basic boolean todo lists
(sequence "BACKLOG" "NEXT" "DOING" "FOLLOW-UP" "BLOCKED" "|" "DONE" "DELEGATED"))) ;; fancy-pants kanban style, multi-state todo lists
(setq org-tag-alist '(("@work" . ?w) ("@home" . ?h) ("hobby" . ?h) ("client" . ?c))) ;; not in use, but one day...
(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 elements that are really missing from this config that I use everywhere are sly and geiser. I prefer to keep my Common Lisp and Scheme stuff encapsulated to personal-pizza.el, since it varies from machine to machine.
An example of what that looks like, though:
;; Common Lisp support
(use-package sly)
(setq inferior-lisp-program "/usr/local/bin/sbcl --noinform") ;; this is the bit that changes from machine to machine, usually.
;; Scheme support for chibi, chicken, and racket
(use-package geiser
:config
(setq geiser-racket-binary "/usr/local/bin/racket")
(setq geiser-chicken-binary "/usr/local/bin/csi")
(setq geiser-chibi-binary "/usr/local/bin/chibi-scheme")
(setq geiser-active-implementations '(chicken chibi racket)))
;; and a fun little snippet that causes emacs to launch in full screen
(custom-set-variables
'(initial-frame-alist (quote ((fullscreen . maximized)))))