#+TITLE: GNU Emacs Configuration #+DATE: <2017-08-11 Fri> #+AUTHOR: Yann Herklotz #+EMAIL: ymherklotz@gmail.com ** Introduction This is my GNU Emacs Configuration that is mostly focused on C++ development, but also has support for Python, F#, Haskell and Clojure. #+BEGIN_SRC emacs-lisp (setq user-full-name "Yann Herklotz") (setq user-mail-address "ymherklotz@gmail.com") #+END_SRC ** Setup *** Repositories Defining all the package repositories that are going to be used. - ~gnu~ :: The default package repository for emacs - ~melpa~ :: Contains a lot of additional packages for emacs that are made by the community. - ~melpa-stable~ :: The stable melpa repository that only contains that full versions for packages. This repository will be used for packages that maybe get updated often, so that they do not break the config. - ~org~ :: org package repository that contains many packages to extend org-mode. #+BEGIN_SRC emacs-lisp (require 'package) (defvar gnu '("gnu" . "https://elpa.gnu.org/packages/")) (defvar melpa '("melpa" . "https://melpa.org/packages/")) (defvar melpa-stable '("melpa-stable" . "https://stable.melpa.org/packages/")) (defvar org-elpa '("org" . "https://orgmode.org/elpa/")) #+END_SRC These packages are then added to the list of package archives. #+BEGIN_SRC emacs-lisp (setq package-archives nil) (add-to-list 'package-archives melpa-stable t) (add-to-list 'package-archives melpa t) (add-to-list 'package-archives gnu t) (add-to-list 'package-archives org-elpa t) #+END_SRC Initialise the packages and if the directories don't exist, create them. #+BEGIN_SRC emacs-lisp (setq package-enable-at-startup nil) (package-initialize) #+END_SRC Use ~use-package~ to manage other packages, and improve load times. #+BEGIN_SRC emacs-lisp (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package)) (require 'use-package) (setq use-package-always-ensure t) #+END_SRC *** Server Configuration Server so that a client can connect to it. This makes using emacs much smoother, as frames can be opened using the emacsclient. #+BEGIN_SRC emacs-lisp (setq server-socket-file "/tmp/emacs1000/server") (setq server-socket-dir "/tmp/emacs1000") (unless (file-exists-p server-socket-file) (server-start)) #+END_SRC *** GC Threshold Threshold for faster startup. #+BEGIN_SRC emacs-lisp (setq gc-cons-threshold 500000000) #+END_SRC Move the backup files into the temporaty directory so that they are out of the way. #+BEGIN_SRC emacs-lisp (setq backup-directory-alist `((".*" . ,temporary-file-directory))) (setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t))) #+END_SRC *** General Configuration **** UI Editor specific options such as adding line numbers. Disable UI that starts when starting emacs and also set the y or n instead of yes or no. Also stop the start up message from popping up and enter the scratch buffer instead. #+BEGIN_SRC emacs-lisp (setq inhibit-startup-message t) (tool-bar-mode -1) (menu-bar-mode -1) (fset 'yes-or-no-p 'y-or-n-p) #+END_SRC **** Global settings Make emacs follow symlinks every time, this means that it will open the actual file and go to where the file is actually stored instead of editing it through the symlink. This enables the use of git and other version control when editing the file. #+BEGIN_SRC emacs-lisp (setq vc-follow-symlinks t) #+END_SRC Make it easier to refresh the buffer by setting it to ~~. #+BEGIN_SRC emacs-lisp (global-set-key (kbd "") 'revert-buffer) #+END_SRC This stops paren mode with interfering with the modeline. #+BEGIN_SRC emacs-lisp (show-paren-mode 'expression) #+END_SRC Revert the buffer automatically when a file changes on disc. This is useful when monitoring a file such as a log file. It will also do this silently. #+BEGIN_SRC emacs-lisp (global-auto-revert-mode 1) (setq auto-revert-verbose nil) #+END_SRC Disable tabs, I want to move towards only using spaces everywhere as that is my preferred style. This is just personal preference though. #+BEGIN_SRC emacs-lisp (setq-default indent-tabs-mode nil) (setq-default tab-width 4) (setq-default python-indent-offset 4) #+END_SRC Set the line number display very high so that it is always shown in the modeline and set the column width to 80. #+BEGIN_SRC emacs-lisp (setq line-number-display-limit 2000000) (setq-default fill-column 80) #+END_SRC **** Font Set the font to Hack, which is an opensource monospace font designed for programming and looking at source code. #+BEGIN_SRC emacs-lisp (set-default-font "Hack-11") (setq default-frame-alist '((font . "Hack-11"))) #+END_SRC **** Shell #+BEGIN_SRC emacs-lisp (use-package eshell :ensure nil :bind (("C-c e" . eshell))) #+END_SRC ** Social *** Mail ~mu4e~ is automatically in the load path when installed through a package manager. For archlinux, the command to install mu4e is: #+BEGIN_SRC shell pacman -S mu #+END_SRC which comes with mu. Set the email client to be mu4e in emacs, and set the correct mail directory. As I am downloading all the mailboxes, there will be duplicates, which can be ignored in searches by setting ~mu4e-headers-skip-duplicates~. Also delete messages when they are sent, and don't copy them over to the sent directory, as Gmail will do that for us. To download the mail using imap, I use ~mbsync~, which downloads all mail with the ~-a~ flag. Finally, remove buffers when an email has been sent. #+BEGIN_SRC emacs-lisp (use-package mu4e :ensure nil :commands mu4e :config (setq mail-user-agent 'mu4e-user-agent) (setq mu4e-maildir "~/.mail") (setq mu4e-headers-skip-duplicates t) (setq mu4e-sent-messages-behavior 'delete) (setq mu4e-get-mail-command "mbsync -a") (setq message-kill-buffer-on-exit t) (setq mu4e-completing-read-function 'completing-read) (setq mu4e-context-policy 'pick-first) (setq mu4e-confirm-quit nil) ;;; Mail directory shortcuts (setq mu4e-maildir-shortcuts '(("/gmail/Inbox" . ?g) ("/imperial/Inbox" . ?i))) (setq mu4e-contexts `( ,(make-mu4e-context :name "Gmail" :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to "ymherklotz@gmail.com"))) :vars '((user-mail-address . "ymherklotz@gmail.com") (user-full-name . "Yann Herklotz") (mu4e-sent-folder . "/gmail/[Gmail]/Sent Mail") (mu4e-drafts-folder . "/gmail/[Gmail]/Drafts") (mu4e-trash-folder . "/gmail/[Gmail]/Trash") (mu4e-refile-folder . "/gmail/[Gmail]/All Mail") (smtpmail-smt-user . "ymherklotz@gmail.com") (smtpmail-local-domain . "gmail.com") (smtpmail-default-smtp-server . "smtp.gmail.com") (smtpmail-smtp-server . "smtp.gmail.com") (smtpmail-smtp-service . 587))) ,(make-mu4e-context :name "Imperial" :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to "ymh15@ic.ac.uk"))) :vars '((user-mail-address . "ymh15@ic.ac.uk") (user-full-name . "Yann Herklotz") (mu4e-sent-folder . "/imperial/Sent") (mu4e-drafts-folder . "/imperial/Drafts") (mu4e-trash-folder . "/imperial/Trash") (mu4e-refile-folder . "/imperial/Archive") (smtpmail-smt-user . "ymh15@ic.ac.uk") (smtpmail-local-domain . "cc.ic.ac.uk") (smtpmail-default-smtp-server . "smtp.cc.ic.ac.uk") (smtpmail-smtp-server . "smtp.cc.ic.ac.uk") (smtpmail-smtp-service . 587)))))) #+END_SRC Setting up ~smtp~ to send messages using gmail. #+BEGIN_SRC emacs-lisp (use-package smtpmail :ensure nil :config (setq message-send-mail-function 'smtpmail-send-it starttls-use-gnutls t)) #+END_SRC ** Utility *** Discoverability Used to display what every key combination does when starting to enter it. #+BEGIN_SRC emacs-lisp (use-package which-key :diminish which-key-mode :config (which-key-mode)) #+END_SRC *** Navigation **** Ace Windows #+BEGIN_SRC emacs-lisp (use-package ace-window :bind (("C-x o" . ace-window))) #+END_SRC **** Avy #+BEGIN_SRC emacs-lisp (use-package avy :bind (("C-:" . avy-goto-char) ("C-'" . avy-goto-char-2))) #+END_SRC **** Ivy #+BEGIN_SRC emacs-lisp (use-package ivy :bind (("C-s" . swiper) ("M-x" . counsel-M-x) ("C-x C-f" . counsel-find-file) ("C-c g" . counsel-git) ("C-c j" . counsel-git-grep) ("C-c k" . counsel-ag) ("C-c C-r" . ivy-resume) ("C-x b" . counsel-ibuffer)) :config (setq ivy-use-virtual-buffers t) (setq ivy-count-format "(%d/%d) ")) #+END_SRC **** Winner Mode #+BEGIN_SRC emacs-lisp (setq winner-mode t) #+END_SRC **** Beacon mode #+BEGIN_SRC emacs-lisp (use-package beacon :config (beacon-mode 1)) #+END_SRC **** Dumb Jump #+BEGIN_SRC emacs-lisp (use-package dumb-jump :commands dumb-jump) #+END_SRC *** Visual **** All the icons #+BEGIN_SRC emacs-lisp (use-package all-the-icons) #+END_SRC **** Treemacs #+BEGIN_SRC emacs-lisp (use-package treemacs :commands treemacs :bind (("C-c t" . treemacs))) #+END_SRC *** Editing **** Hungry Delete #+BEGIN_SRC emacs-lisp (use-package hungry-delete :config (global-hungry-delete-mode)) #+END_SRC **** SmartParens #+BEGIN_SRC emacs-lisp (use-package smartparens :bind (("M-[" . sp-backward-unwrap-sexp) ("M-]" . sp-unwrap-sexp) ("C-M-f" . sp-forward-sexp) ("C-M-b" . sp-backward-sexp) ("C-M-d" . sp-down-sexp) ("C-M-a" . sp-backward-down-sexp) ("C-M-e" . sp-up-sexp) ("C-M-u" . sp-backward-up-sexp) ("C-M-t" . sp-transpose-sexp) ("C-M-n" . sp-next-sexp) ("C-M-p" . sp-previous-sexp) ("C-M-k" . sp-kill-sexp) ("C-M-w" . sp-copy-sexp) ("C-" . sp-forward-slurp-sexp) ("C-" . sp-forward-barf-sexp) ("C-M-" . sp-backward-slurp-sexp) ("C-M-" . sp-backward-barf-sexp) ("M-D" . sp-splice-sexp) ("C-]" . sp-select-next-thing-exchange) ("C-" . sp-select-previous-thing) ("C-M-]" . sp-select-next-thing) ("M-F" . sp-forward-symbol) ("M-B" . sp-backward-symbol)) :hook ((minibuffer-setup) . turn-on-smartparens-strict-mode) :config (require 'smartparens-config) (show-smartparens-global-mode +1) (smartparens-global-mode 1) (sp-with-modes '(c-mode c++-mode) (sp-local-pair "{" nil :post-handlers '(("||\n[i]" "RET"))) (sp-local-pair "/*" "*/" :post-handlers '((" | " "SPC") ("* ||\n[i]" "RET"))))) #+END_SRC **** Undo Tree #+BEGIN_SRC emacs-lisp (use-package undo-tree :bind (("C-x u" . undo-tree-visualize) ("C-_" . undo-tree-undo) ("M-_" . undo-tree-redo)) :diminish undo-tree-mode :config (global-undo-tree-mode)) #+END_SRC **** Whitespace #+BEGIN_SRC emacs-lisp (use-package whitespace :bind (("C-x w" . whitespace-mode))) #+END_SRC ** Writing *** Spellcheck in emacs #+BEGIN_SRC emacs-lisp (defun spell-buffer-german () (interactive) (ispell-change-dictionary "de_DE") (flyspell-buffer)) (defun spell-buffer-english () (interactive) (ispell-change-dictionary "en_US") (flyspell-buffer)) (use-package ispell :config (when (executable-find "hunspell") (setq-default ispell-program-name "hunspell") (setq ispell-really-hunspell t)) ;; (setq ispell-program-name "aspell" ;; ispell-extra-args '("--sug-mode=ultra")) :bind (("C-c N" . spell-buffer-german) ("C-c n" . spell-buffer-english))) #+END_SRC *** Word Wrapping Wrap words when in text mode. #+BEGIN_SRC emacs-lisp (dolist (hook '(text-mode-hook)) (add-hook hook (lambda () (flyspell-mode 1) (visual-line-mode 1)))) #+END_SRC *** Markdown Markdown is the standard for writing documentation. This snippet loads GFM (Github Flavoured Markdown) style. #+BEGIN_SRC emacs-lisp (use-package markdown-mode :commands (markdown-mode gfm-mode) :mode (("README\\.md\\'" . gfm-mode) ("\\.md\\'" . markdown-mode) ("\\.markdown\\'" . markdown-mode)) :init (setq markdown-command "multimarkdown")) #+END_SRC *** Org Agenda setup for org mode, pointing to the write files. #+BEGIN_SRC emacs-lisp (setq org-agenda-files (quote ("~/Dropbox/org"))) (setq org-log-into-drawer t) (setq org-log-done "note") (setq org-hide-leading-stars t) (setq org-confirm-babel-evaluate nil) (setq org-directory (expand-file-name "~/Dropbox/org")) (setq org-default-notes-file (concat org-directory "/notes.org")) (setq org-image-actual-width nil) #+END_SRC Set global keys for org mode to access agenda. #+BEGIN_SRC emacs-lisp (global-set-key "\C-cl" 'org-store-link) (global-set-key "\C-ca" 'org-agenda) (global-set-key "\C-cc" 'org-capture) (global-set-key "\C-cb" 'org-iswitchb) (define-key global-map "\C-cc" 'org-capture) #+END_SRC Set up ob for executing code blocks #+BEGIN_SRC emacs-lisp (use-package ox-taskjuggler :ensure nil :load-path "~/.emacs.d/personal") (use-package ob :ensure nil :config (org-babel-do-load-languages 'org-babel-load-languages '( (emacs-lisp . t) (js . t) (java . t) (haskell . t) (python . t) (ruby . t) (org . t) (matlab . t) (ditaa . t) (clojure . t) ))) #+END_SRC #+BEGIN_SRC emacs-lisp (setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) #+END_SRC Exporting to html needs htmlize. #+BEGIN_SRC emacs-lisp (use-package htmlize :commands (htmlize-file htmlize-buffer htmlize-region htmlize-many-files htmlize-many-files-dired htmlize-region-save-screenshot)) #+END_SRC *** Writeroom #+BEGIN_SRC emacs-lisp (use-package writeroom-mode :commands writeroom-mode) #+END_SRC ** Programming My emacs configuration is mostly focused on programming, therefore there is a lot of different language support. *** Version Control and Project Management **** Magit #+BEGIN_SRC emacs-lisp (use-package magit :bind (("C-x g" . magit-status))) #+END_SRC **** Projectile #+BEGIN_SRC emacs-lisp (use-package projectile :diminish projectile-mode :config (projectile-global-mode 1) (setq projectile-enable-caching t)) (use-package counsel-projectile :config (counsel-projectile-mode t)) #+END_SRC *** Language Support **** C++ Setting up CC mode with a hook that uses my settings. #+BEGIN_SRC emacs-lisp (use-package cc-mode :config (add-to-list 'auto-mode-alist '("\\.h\\'" . c++-mode)) (setq c-default-style "linux" c-basic-offset 4 c-indent-level 4) (defun my-c++-mode-hook () (c-set-offset 'inline-open 0) (c-set-offset 'inline-close 0) (c-set-offset 'innamespace 0) (c-set-offset 'arglist-cont-nonempty 8) (setq indent-tabs-mode nil)) (add-hook 'c-mode-hook 'my-c++-mode-hook) (add-hook 'c++-mode-hook 'my-c++-mode-hook) (define-key c-mode-map (kbd "C-c C-c") 'comment-or-uncomment-region)) #+END_SRC Adding C headers to company backend for completion. #+BEGIN_SRC emacs-lisp (use-package irony :config (add-hook 'c++-mode-hook 'irony-mode) (add-hook 'c-mode-hook 'irony-mode) (add-hook 'objc-mode-hook 'irony-mode) (defun my-irony-mode-hook () (define-key irony-mode-map [remap completion-at-point] 'irony-completion-at-point-async) (define-key irony-mode-map [remap complete-symbol] 'irony-completion-at-point-async)) (add-hook 'irony-mode-hook 'my-irony-mode-hook) (add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)) (use-package company-irony) (use-package flycheck-irony :config (add-hook 'c++-mode-hook #'flycheck-irony-setup)) (use-package company-c-headers :config (add-to-list 'company-backends 'company-c-headers) (add-to-list 'company-backends 'company-irony) (add-hook 'irony-mode-hook 'company-irony-setup-begin-commands)) #+END_SRC Using clang format to format the region that is currently being selected (need to install clang format script). #+BEGIN_SRC emacs-lisp (use-package clang-format :bind (("C-c i" . 'clang-format-region) ("C-c u" . 'clang-format-buffer))) #+END_SRC **** Clojure Using Cider for clojure environment. #+BEGIN_SRC emacs-lisp (use-package cider :commands cider-mode :pin melpa-stable :config (setq cider-repl-display-help-banner nil)) #+END_SRC Adding hook to clojure mode to enable strict parentheses mode. #+BEGIN_SRC emacs-lisp (add-hook 'clojure-mode-hook 'turn-on-smartparens-strict-mode) #+END_SRC **** CMake #+BEGIN_SRC emacs-lisp (use-package cmake-mode :commands cmake-mode :config (setq auto-mode-alist (append '(("CMakeLists\\.txt\\'" . cmake-mode)) '(("\\.cmake\\'" . cmake-mode)) auto-mode-alist)) (autoload 'cmake-mode "~/CMake/Auxiliary/cmake-mode.el" t)) #+END_SRC **** Emacs Lisp Adding strict parentheses to emacs lisp. #+BEGIN_SRC emacs-lisp (add-hook 'emacs-lisp-mode-hook 'turn-on-smartparens-strict-mode) #+END_SRC **** F# F# mode for uni work. #+BEGIN_SRC emacs-lisp (use-package fsharp-mode :commands fsharp-mode) #+END_SRC **** Haskell Haskell mode with company mode completion. #+BEGIN_SRC emacs-lisp (use-package haskell-mode :commands haskell-mode) #+END_SRC **** Python Elpy package for python, which provides an IDE type environment for python. #+BEGIN_SRC emacs-lisp (use-package elpy :commands python-mode :config (elpy-enable) (setq py-python-command "python3") (setq python-shell-interpreter "python3")) (with-eval-after-load 'python (defun python-shell-completion-native-try () "Return non-nil if can trigger native completion." (let ((python-shell-completion-native-enable t) (python-shell-completion-native-output-timeout python-shell-completion-native-try-output-timeout)) (python-shell-completion-native-get-completions (get-buffer-process (current-buffer)) nil "_")))) #+END_SRC **** JSON JSON files should be opened in js-mode. #+BEGIN_SRC emacs-lisp (add-to-list 'auto-mode-alist '("\\.json\\'" . js-mode)) #+END_SRC **** Shell #+BEGIN_SRC emacs-lisp (setq sh-basic-offset 2) (setq sh-indentation 2) #+END_SRC *** Completion Support **** Company #+BEGIN_SRC emacs-lisp (use-package company :config (add-hook 'after-init-hook 'global-company-mode) (setq company-backends (delete 'company-semantic company-backends)) (define-key c-mode-map (kbd "C-c n") 'company-complete) (define-key c++-mode-map (kbd "C-c n") 'company-complete) (setq company-dabbrev-downcase 0)) #+END_SRC **** Flycheck Enabling global flycheck support. #+BEGIN_SRC emacs-lisp (use-package flycheck :diminish flycheck-mode :config (global-flycheck-mode)) #+END_SRC **** Yasnippets #+BEGIN_SRC emacs-lisp (use-package yasnippet :hook ((org-mode cc-mode) . yas-minor-mode) :diminish yas-minor-mode :config (yas-minor-mode 1)) #+END_SRC ** Look and Feel #+BEGIN_SRC emacs-lisp (defadvice load-theme (before theme-dont-propagate activate) (mapc #'disable-theme custom-enabled-themes)) ;; (use-package color-theme-sanityinc-tomorrow) ;; (use-package leuven-theme) ;; (use-package zenburn-theme) ;; (use-package gruvbox-theme) ;; (use-package material-theme) ;; (use-package monokai-theme) ;; (use-package plan9-theme) (use-package telephone-line :init (setq telephone-line-primary-left-separator 'telephone-line-cubed-left telephone-line-secondary-left-separator 'telephone-line-cubed-hollow-left telephone-line-primary-right-separator 'telephone-line-cubed-right telephone-line-secondary-right-separator 'telephone-line-cubed-hollow-right) (setq telephone-line-height 24 telephone-line-evil-use-short-tag t)) (if (daemonp) (add-hook 'after-make-frame-functions (lambda (frame) (select-frame frame) (load-theme 'sanityinc-tomorrow-bright t) (telephone-line-mode 1) (toggle-scroll-bar -1))) (progn (load-theme 'sanityinc-tomorrow-bright t) (telephone-line-mode 1) (toggle-scroll-bar -1))) #+END_SRC ** My Code #+BEGIN_SRC emacs-lisp (defun y/swap-windows () "Swaps two windows and leaves the cursor in the original one" (interactive) (ace-swap-window) (aw-flip-window)) (defun y/fsharp-reload-file () "Reloads the whole file when in fsharp mode." (interactive) (fsharp-eval-region (point-min) (point-max))) (defun y/exit-emacs-client () "consistent exit emacsclient. If not in emacs client, echo a message in minibuffer, don't exit emacs. If in server mode and editing file, do C-x # server-edit else do C-x 5 0 delete-frame" (interactive) (if server-buffer-clients (server-edit) (delete-frame))) (defun y/beautify-json () (interactive) (let ((b (if mark-active (min (point) (mark)) (point-min))) (e (if mark-active (max (point) (mark)) (point-max)))) (shell-command-on-region b e "python -m json.tool" (current-buffer) t))) (defun y/set-theme (theme) (load-theme theme t) (telephone-line-mode 1) (toggle-scroll-bar -1)) #+END_SRC Setting up my keybindings #+BEGIN_SRC emacs-lisp (define-prefix-command 'y-map) (global-set-key (kbd "C-c y") 'y-map) (global-set-key (kbd "C-c q") 'y/exit-emacs-client) (define-key y-map (kbd "s") 'y/swap-windows) (define-key y-map (kbd "j") 'y/beautify-json) (define-key y-map (kbd "1") (lambda () (interactive) (y/set-theme 'sanityinc-tomorrow-bright))) (define-key y-map (kbd "2") (lambda () (interactive) (y/set-theme 'zenburn))) (define-key y-map (kbd "3") (lambda () (interactive) (y/set-theme 'leuven))) (define-key y-map (kbd "4") (lambda () (interactive) (y/set-theme 'ample))) (define-key y-map (kbd "5") (lambda () (interactive) (y/set-theme 'ample-flat))) (add-hook 'fsharp-mode-hook (lambda () (local-set-key (kbd "C-c C-c") #'y/fsharp-reload-file))) #+END_SRC Registers #+BEGIN_SRC emacs-lisp (set-register ?l (cons 'file "~/.emacs.d/loader.org")) (set-register ?n (cons 'file "~/Dropbox/org/note.org")) (set-register ?t (cons 'file "~/Dropbox/org/todo.org")) (set-register ?h (cons 'file "~/Dropbox/org/today.org")) #+END_SRC ** Conclusion Setting the gc-cons threshold back to what it was at the beginning. #+BEGIN_SRC emacs-lisp (setq gc-cons-threshold 10000000) #+END_SRC