emacs.d
emacs.d copied to clipboard
My personal emacs setup.
#+TITLE: Echosa's Emacs Configuration #+AUTHOR: Echosa #+OPTIONS: toc:4 h:4
-
Introduction Inspired by [[http://sachachua.com/blog/2012/06/literate-programming-emacs-configuration-file/][Sacha Chua]], I have moved my Emacs configuration into an organized and descriptive [[http://orgmode.org][org-mode]] file. What you are reading now is, in fact, my Emacs configuration file.
Well, sort of.
How this works is based around a part of ~org-mode~ called ~org-babel~. ~org-babel~ allows ~org-mode~ to execute code that is embedded into a .org file. If you look at the [[https://github.com/echosa/emacs.d/blob/master/init.el][actual init.el file]] that my Emacs loads, you'll see that all it does is load the .org file containing my configuration (the one you're reading now) and parse it through org-babel to execute only the blocks of elisp that make up the actual configuration, while ignoring the extra documentation and narrative, like this introduction section.
If you're wondering about performance, org-babel doesn't do this parse every time I open Emacs. Instead, it sees that I'm trying to load ~emacs-config.org~ and checks for the existence of ~emacs-config.el~. If it doesn't find that file, or finds an out of date version, only then does it parse the .org file to create a new .el file. This means there's a bit of a slow startup the first time after the org-mode file is changes, but after that there's no noticeable change in performance (at least on my machine). I have accounted for this one-off performance hit, however, by automatically regenerating ~emacs-config.el~ when I save ~emacs-config.org~. You'll see this later.
The only other source of major slowdown, usually only on the first run, is the installation of packages. On a fresh install of Emacs using this configuration, the first time Emacs is run ~org-babel~ parses the .org file and also installs all the external packages required by my config. This makes for a slow first-time startup, but the benefit is that all of the configuration, installation, and setup is done for me automatically. I don't have to manually set anything up, which, when setting up other editors, can often take much longer than the first run of this config. Plus, it's more work to have to manually set things up, so I'll take a one-time slowdown in trade of automatically having my Emacs setup installed and ready for me.
Anyway, what follows is my actual Emacs configuration, embedded into a descriptive narrative.
-
Installation ** Get from GitHub First, you need to get the config from GitHub. I recommend actually cloning instead of just downloading a zip file, because a cloned repo will be easier to update.
First, delete, move, or rename your existing Emacs configuration (both the ~.emacs.d/~ directory and your ~.emacs~ init file). Next, clone the repository into your home directory:
~$ git clone [email protected]:echosa/emacs.d.git ~/.emacs.d~
** Start Emacs Simply run Emacs and wait a bit! All the necessary packages will download and install automatically and everything will be configured. This is because of the ~use-package~ package's ~:ensure~ flag. That flag tells Emacs to download and install the package if it is not already installed. Because of this, the first time you start Emacs make take a few seconds as all the packages are downloaded and installed. Subsequent starts will not take so long.
- Configuration Maintenance In order to make maintaining this config easier, I've made this ~echosa-export-config~ function which will export the proper ~emacs-config.el~ and ~README.org~ files when ~emacs-config.org~ is updated and saved. #+BEGIN_SRC emacs-lisp (defun echosa-export-config () (when (string= (buffer-name (current-buffer)) "emacs-config.org") (let ((org-file "~/.emacs.d/emacs-config.org") (elisp-file "~/.emacs.d/emacs-config.el") (readme-file "~/.emacs.d/README.org")) (org-babel-tangle-file org-file elisp-file "emacs-lisp") (copy-file org-file readme-file t) (message "Config export complete!")))) (add-hook 'after-save-hook 'echosa-export-config) #+END_SRC
- Packages External and third-party packages are great. They make adding new things to Emacs much nicer and less complicated. ** Repositories We need to set up the package repositories for Emacs' package manager. #+BEGIN_SRC emacs-lisp (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "https://melpa.org/packages/") ("melpa-stable" . "https://stable.melpa.org/packages/"))) #+END_SRC
** use-package I've come to appreciate the [[https://github.com/jwiegley/use-package][use-package]] way of handling package management. Everything is nice, clean, and compact. Best of all, all setup for a package is self-contained within a ~use-package~ declaration, so no need to hunt through your whole config to find related parts (hooks in one place, key bindings in another, etc.)
Since I use ~use-package~ to install all the other packages, this is the only place where I pull in and use package.el. I only do that to ensure that ~use-package~ is installed and ready to go. #+BEGIN_SRC emacs-lisp (require 'package) (package-initialize) (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package))
(eval-when-compile (require 'use-package)) #+END_SRC
-
General Setup Here, I change some generic Emacs behavior. These are all things that aren't tied to a specific mode or programming language. Most of these are self-explanatory. However, for more info, you can look them up with Emacs' built-in help. That will do a better job of explaining that I can, plus there's no need for me to reiterate it all here. #+BEGIN_SRC emacs-lisp (temp-buffer-resize-mode 0) (add-hook 'before-save-hook 'time-stamp) (setq fill-column 80) (setq scroll-conservatively 101) (setq case-fold-search t) (setq case-replace t) (setq display-buffer-reuse-frames t) (setq display-time-24hr-format nil) (setq display-time-day-and-date t) (setq large-file-warning-threshold nil) (setq truncate-partial-width-windows nil) #+END_SRC Allow ~a~ to be used in dired to reuse the buffer instead of creating new buffers for every directory. #+BEGIN_SRC emacs-lisp (put 'dired-find-alternate-file 'disabled nil) #+END_SRC Don't load outdated complied files. #+BEGIN_SRC emacs-lisp (setq load-prefer-newer t) #+END_SRC Make sure Emacs can find and run commands on the PATH. #+BEGIN_SRC emacs-lisp (when (memq window-system '(mac ns x)) (setenv "PATH" (concat "/usr/local/bin:" (getenv "PATH"))) (setq exec-path (append '("/usr/local/bin") exec-path))) #+END_SRC Remember open files and buffers between sessions. #+BEGIN_SRC emacs-lisp (desktop-save-mode 1) #+END_SRC ** Backup and Auto Save I don't like Emacs littering and leaving a bunch of temporary files all over the place, so here I tell it to keep all those files in one place. #+BEGIN_SRC emacs-lisp (setq auto-save-file-name-transforms '((".*" "~/.emacs.d/.tmp/" nil))) (setq auto-save-list-file-prefix "~/.emacs.d/.tmp/.saves-") (setq backup-directory-alist '(("" . "~/.emacs.d/.tmp"))) #+END_SRC ** Things I don't want to see *** Scroll bars #+BEGIN_SRC emacs-lisp (when (fboundp 'toggle-scroll-bar) (toggle-scroll-bar nil)) #+END_SRC *** Tool bar #+BEGIN_SRC emacs-lisp (tool-bar-mode -1) #+END_SRC *** Menu bar #+BEGIN_SRC emacs-lisp (menu-bar-mode -1) #+END_SRC *** Splash screen It's unnecessary, really. #+BEGIN_SRC emacs-lisp (setq inhibit-startup-screen t) #+END_SRC ** Things I do want to see *** Highlight current region/selection #+BEGIN_SRC emacs-lisp (transient-mark-mode t) #+END_SRC *** Syntax highlighting #+BEGIN_SRC emacs-lisp (global-font-lock-mode t) #+END_SRC *** Column number #+BEGIN_SRC emacs-lisp (column-number-mode t) #+END_SRC *** Show matching parenthesis #+BEGIN_SRC emacs-lisp (show-paren-mode t) #+END_SRC *** Blinking cursor #+BEGIN_SRC emacs-lisp (setq blink-cursor-mode t) #+END_SRC *** Show empty lines #+BEGIN_SRC emacs-lisp (setq indicate-empty-lines t) #+END_SRC *** Highlight the current light #+BEGIN_SRC emacs-lisp (global-hl-line-mode 1) #+END_SRC *** Line Numbers I like line numbers. They help quite a bit with moving around. #+BEGIN_SRC emacs-lisp (global-display-line-numbers-mode) #+END_SRC *** Visible Bell I don't want to hear a blip every time I do something wrong, so I'm turning on the visible bell. #+BEGIN_SRC emacs-lisp (setq visible-bell t) #+END_SRC
-
Uniquify If I have two buffers open with two files that have the same name, (e.g. two different README files from two different projects), Emacs will, by default, name the buffers ~README~ and ~README<1>~. This is useless. Therefore, I turn on uniquify and use it to name buffers with the same file name based on their parent directories: ~README
~ and ~README ~. #+BEGIN_SRC emacs-lisp (use-package uniquify :defer t :config (setq uniquify-buffer-name-style 'post-forward-angle-brackets)) #+END_SRC -
Ido and Icomplete Here I configure Ido and Icomplete. Ido gives improved file finding and buffer switching. Icomplete gives improved command execution with ~M-x~. #+BEGIN_SRC emacs-lisp (use-package icomplete :config (icomplete-mode)) (use-package ido :config (ido-mode 1) (ido-everywhere 1) (setq ido-enable-flex-matching t)) #+END_SRC
-
Evil Update: At the moment, I have Evil disabled. I'm seeing how I get by without it. I might learn that I no longer need or want it. However, just in case, I leaving my config here, disabled through ~use-package~. (Have I mentioned how awesome ~use-package~ is?)
Call me heathen if you wish, but I prefer Vim navigation keys. Also, I want Ido buffer switching and file finding when using Vim's ~:b~ and ~:e~.
[[https://gitorious.org/evil/pages/Home][Evil website]] #+BEGIN_SRC emacs-lisp (use-package evil :disabled :ensure t :after (key-chord) :config (setq evil-default-cursor '(t)) (evil-mode 1) (define-key evil-ex-map "b " 'ido-switch-buffer) (define-key evil-ex-map "e " 'ido-find-file) (key-chord-define evil-insert-state-map "jk" 'evil-normal-state) (key-chord-define evil-motion-state-map "jk" 'evil-normal-state) (key-chord-define evil-visual-state-map "jk" 'evil-normal-state) (key-chord-define evil-emacs-state-map "jk" 'evil-normal-state)) #+END_SRC Using ~key-chord-mode~, I have the vim equivalent of ~imap jk <Esc>~, which allows me to use ~jk~ instead of ~Esc~ to get out of insert mode. #+BEGIN_SRC emacs-lisp (use-package key-chord :disabled :ensure t :config (key-chord-mode 1)) #+END_SRC To make things even easier, I set up a "leader key" of ~Space~, so that I can type ~Space
~ to run a command. For instance, ~Space x~ instead of ~M-x~ to execute commands. #+BEGIN_SRC emacs-lisp (use-package evil-leader :disabled :ensure t :after (evil) :config (evil-leader/set-leader "<SPC>") (evil-leader/set-key "x" 'execute-extended-command) (evil-leader/set-key ":" 'eval-expression) (evil-leader/set-key "k" 'ido-kill-buffer) (evil-leader/set-key "p" 'projectile-commander) (evil-leader/set-key "d" 'dired) (evil-leader/set-key "e" 'er/expand-region) (evil-leader/set-key "m" 'mc/mark-more-like-this-extended) (evil-leader/set-key "s" 'string-inflection-toggle) (evil-leader/set-key "r" 'xref-find-definitions) (evil-leader/set-key "?" 'xref-find-references) (global-evil-leader-mode)) #+END_SRC Let's make sure we have "surround" support. #+BEGIN_SRC emacs-lisp (use-package evil-surround :disabled :ensure t :config (global-evil-surround-mode 1)) #+END_SRC Finally, there are some modes that I want to always be in Emacs mode instead of Evil. Major modes: #+BEGIN_SRC emacs-lisp (setq evil-emacs-state-modes '(archive-mode bbdb-mode bookmark-bmenu-mode bookmark-edit-annotation-mode browse-kill-ring-mode bzr-annotate-mode calc-mode cfw:calendar-mode completion-list-mode Custom-mode debugger-mode delicious-search-mode desktop-menu-blist-mode desktop-menu-mode doc-view-mode dvc-bookmarks-mode dvc-diff-mode dvc-info-buffer-mode dvc-log-buffer-mode dvc-revlist-mode dvc-revlog-mode dvc-status-mode dvc-tips-mode ediff-mode ediff-meta-mode efs-mode Electric-buffer-menu-mode emms-browser-mode emms-mark-mode emms-metaplaylist-mode emms-playlist-mode etags-select-mode fj-mode gc-issues-mode gdb-breakpoints-mode gdb-disassembly-mode gdb-frames-mode gdb-locals-mode gdb-memory-mode gdb-registers-mode gdb-threads-mode gist-list-mode git-rebase-mode gnus-article-mode gnus-browse-mode gnus-group-mode gnus-server-mode gnus-summary-mode google-maps-static-mode ibuffer-mode jde-javadoc-checker-report-mode magit-popup-mode magit-popup-sequence-mode magit-commit-mode magit-revision-mode magit-diff-mode magit-key-mode magit-log-mode magit-mode magit-reflog-mode magit-show-branches-mode magit-branch-manager-mode magit-stash-mode magit-status-mode magit-wazzup-mode magit-refs-mode mh-folder-mode monky-mode mu4e-main-mode mu4e-headers-mode mu4e-view-mode notmuch-hello-mode notmuch-search-mode notmuch-show-mode occur-mode org-agenda-mode package-menu-mode proced-mode rcirc-mode rebase-mode recentf-dialog-mode reftex-select-bib-mode reftex-select-label-mode reftex-toc-mode sldb-mode slime-inspector-mode slime-thread-control-mode slime-xref-mode sr-buttons-mode sr-mode sr-tree-mode sr-virtual-mode tar-mode tetris-mode tla-annotate-mode tla-archive-list-mode tla-bconfig-mode tla-bookmarks-mode tla-branch-list-mode tla-browse-mode tla-category-list-mode tla-changelog-mode tla-follow-symlinks-mode tla-inventory-file-mode tla-inventory-mode tla-lint-mode tla-logs-mode tla-revision-list-mode tla-revlog-mode tla-tree-lint-mode tla-version-list-mode twittering-mode urlview-mode vc-annotate-mode vc-dir-mode vc-git-log-view-mode vc-svn-log-view-mode vm-mode vm-summary-mode w3m-mode wab-compilation-mode xgit-annotate-mode xgit-changelog-mode xgit-diff-mode xgit-revlog-mode xhg-annotate-mode xhg-log-mode xhg-mode xhg-mq-mode xhg-mq-sub-mode xhg-status-extra-mode cider-repl-mode emacsagist-mode elfeed-show-mode elfeed-search-mode notmuch-tree term-mode xref--xref-buffer-mode)) #+END_SRC
-
Winner-mode Winner-mode makes it really easy to handle window changes in Emacs. ~C-c left-arrow~ goes back to the previous window configuration (undo), and ~C-c right-arrow~ goes forward (redo). This is especially helpful for when a popup window ruins your layout. Simply ~C-c left-arrow~ to get back to where you were. #+BEGIN_SRC emacs-lisp (use-package winner :defer 5 :config (winner-mode 1)) #+END_SRC
-
pbcopy Clipboard sharing. Copy in Emacs, paste in OS X, and vice versa.
[[https://github.com/jkp/pbcopy.el][pbcopy source]] #+BEGIN_SRC emacs-lisp (use-package pbcopy :ensure t :defer t :config (turn-on-pbcopy)) #+END_SRC
-
Minibuffer This little snippet adds eldoc support to the minibuffer. Requires Emacs 24.4 or later. [[http://endlessparentheses.com/sweet-new-features-in-24-4.html][Found on EndlessParenthesis.com.]] #+BEGIN_SRC emacs-lisp (add-hook 'eval-expression-minibuffer-setup-hook #'eldoc-mode) #+END_SRC
-
Expand Region This package makes it easy to select regions based on various bounds: words, braces, etc. #+BEGIN_SRC emacs-lisp (use-package expand-region :ensure t :bind (("C-=" . er/expand-region))) #+END_SRC
-
Programming ** General Indent with 4 spaces, not a tab stop. #+BEGIN_SRC emacs-lisp (setq-default c-basic-offset 4) (setq-default tab-width 4) (setq-default indent-tabs-mode nil) #+END_SRC ** Git Magit is awesome.
#+BEGIN_SRC emacs-lisp (use-package magit :ensure t) #+END_SRC
Show changes in the gutter/fringe. #+BEGIN_SRC emacs-lisp (use-package git-gutter-fringe :ensure t :if window-system :config (global-git-gutter-mode))
(use-package git-gutter
:ensure t
:if (not window-system)
:config
(global-git-gutter-mode 1))
#+END_SRC ** Projectile Projectile is, quite simply and objectively, the shit. There's no other way to put it. I consider it pretty much necessary for working with full projects (as opposed to individual, unrelated files).
[[https://github.com/bbatsov/projectile][Projectile on Github]]
#+BEGIN_SRC emacs-lisp
(use-package projectile
:ensure t
:defer 5
:init
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
:config
(projectile-global-mode))
#+END_SRC
** Completion
Who doesn't like a little auto-completion? I choose to use ~company~
instead of ~auto-complete~ (aka ~ac~). This decision is based on
lots of reading about both and comparing/trying out both.
#+BEGIN_SRC emacs-lisp
(use-package company
:ensure t
:bind (("C-
This requires that you have [[https://github.com/ggreer/the_silver_searcher][The Silver Searcher]] installed on your computer. #+BEGIN_SRC emacs-lisp (use-package ag :ensure t) #+END_SRC ** Paredit If you write any form of Lisp and don't use paredit, change that. It does so much for you and helps out in so many ways. I highly recommend it, even though it is quite weird (and, honestly, sometimes frustrating) at first.
[[http://mumble.net/~campbell/emacs/paredit.el][Paredit website]]
[[http://emacsrocks.com/e14.html][Emacs Rocks episode on paredit]] #+BEGIN_SRC emacs-lisp (use-package paredit :ensure t :defer t :hook ((emacs-lisp-mode clojure-mode) . paredit-mode)) #+END_SRC ** PHP Let's start with adding basic PHP handling. #+BEGIN_SRC emacs-lisp (use-package php-mode :ensure t :config (add-hook 'php-mode-hook 'flymake-mode) (add-hook 'php-mode-hook 'php-enable-symfony2-coding-style)) #+END_SRC Next, let's improve completion. This sets up [[https://github.com/xcwen/ac-php][ac-php]] to give better PHP specific completions with ~company~. #+BEGIN_SRC emacs-lisp (use-package company-php :ensure t)
(use-package ac-php :ensure t :after (php-mode company-php) :init (bind-key "C-c ]" 'ac-php-find-symbol-at-point php-mode-map) (bind-key "C-c [" 'ac-php-location-stack-back php-mode-map) :config (add-hook 'php-mode-hook '(lambda () (require 'company-php) (company-mode t) (ac-php-core-eldoc-setup) (make-local-variable 'company-backends) (add-to-list 'company-backends 'company-ac-php-backend)))) #+END_SRC Now, let's set up [[https://github.com/FriendsOfPhp/PHP-CS-Fixer][php-cs-fixer]] so that it automatically fixes our PHP files on save.
Note that I have a config file for this set with ~M-x customize~, not seen in this config. #+BEGIN_SRC emacs-lisp (use-package php-cs-fixer :ensure t :config (require 'cl) (add-hook 'before-save-hook 'php-cs-fixer-before-save)) #+END_SRC Of course, we want to be able to debug our PHP files. That's where [[https://github.com/ahungry/geben][geben]] comes in.
Note that some geben config, like path mappings, I have done with ~M-x customize~, so they do not appear in this file. #+BEGIN_SRC emacs-lisp (use-package geben :ensure t :defer t) #+END_SRC Finally, let's get a lot more detailed and IDE-like functionality with [[https://github.com/emacs-lsp/lsp-mode][LSP in Emacs]].
I currently have this disabled because it isn't working properly. #+BEGIN_SRC emacs-lisp (use-package lsp-mode :disabled :ensure t :commands lsp :init (add-hook 'php-mode-hook #'lsp) )
(use-package lsp-ui :disabled :ensure t :commands lsp-ui-mode)
(use-package company-lsp :disabled :ensure t :commands company-lsp) #+END_SRC ** JavaScript The built-in JS support in Emacs is lacking. #+BEGIN_SRC emacs-lisp (use-package js2-mode :ensure t :defer t :mode "\.js\'")
(use-package json-mode :ensure t :defer t :mode "\.json\'") #+END_SRC ** Web As far as I can tell, ~web-mode~ is the best mode for dealing with web files like HTML, Twig, etc. #+BEGIN_SRC emacs-lisp (use-package web-mode :ensure t :mode (("\.html\'" . web-mode) ("\.twig\'" . web-mode))) #+END_SRC ** YAML Syntax highlighting for YAML files is nice, too. #+BEGIN_SRC emacs-lisp (use-package yaml-mode :ensure t :mode "\.ya?ml\'") #+END_SRC ** Clojure The ultimate experience for [[https://clojure.org/][Clojure]] development: [[https://github.com/clojure-emacs/cider][cider]]! #+BEGIN_SRC emacs-lisp (use-package cider :ensure t) #+END_SRC
-
Org-mode This customizes ~org-mode~ a bit. For instance, I like my org files to have ~auto-fill~ turned on. #+BEGIN_SRC emacs-lisp (defun my-org-mode-hook () (auto-fill-mode)) (add-hook 'org-mode-hook 'my-org-mode-hook) #+END_SRC
-
Fun Packages ** Encouragement #+BEGIN_SRC emacs-lisp (use-package encourage-mode :ensure t :config (encourage-mode t)) #+END_SRC
-
Miscellaneous Functions ** Toggle Window Split This is a quite useful function that will change a frame with two horizontal windows into a frame with two vertical windows and vice versa. #+BEGIN_SRC emacs-lisp ;; http://www.emacswiki.org/emacs/ToggleWindowSplit (defun toggle-window-split () (interactive) (if (= (count-windows) 2) (let* ((this-win-buffer (window-buffer)) (next-win-buffer (window-buffer (next-window))) (this-win-edges (window-edges (selected-window))) (next-win-edges (window-edges (next-window))) (this-win-2nd (not (and (<= (car this-win-edges) (car next-win-edges)) (<= (cadr this-win-edges) (cadr next-win-edges))))) (splitter (if (= (car this-win-edges) (car (window-edges (next-window)))) 'split-window-horizontally 'split-window-vertically))) (delete-other-windows) (let ((first-win (selected-window))) (funcall splitter) (if this-win-2nd (other-window 1)) (set-window-buffer (selected-window) this-win-buffer) (set-window-buffer (next-window) next-win-buffer) (select-window first-win) (if this-win-2nd (other-window 1)))))) (define-key ctl-x-4-map "t" 'toggle-window-split) #+END_SRC
-
Individual Customization Any customization that is machine specific or does not belong in git can go in ~custom.el~. This file is ignored from git and is where all changes from ~M-x customize~ are saved. #+BEGIN_SRC emacs-lisp (setq custom-file "~/.emacs.d/custom.el") (load custom-file 'noerror) #+END_SRC ** Theme I set my theme through ~M-x customize~. That way, it doesn't require changes to this init file.