Init
This is our initialisation script. This basically sets MELPA packages and ensures that user-package is installed on first install. On first install it’s worth setting:
(setq use-package-always-ensure t)
(require 'package)
(package-initialize)
(setq package-enable-at-startup nil)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/"))
;; Bootstrap `use-package'
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(setq custom-file "~/.emacs.d/custom-settings.el")
(load custom-file t)
(setq ad-redefinition-action 'accept)Personal Information
(setq user-full-name "Clint Ryan"
user-mail-address "")Interface
Just some nice user interfaces tweaks. Bread and butter stuff!
(global-linum-mode 1)
(scroll-bar-mode -1)
(setq inhibit-startup-message t)
(setq-default indent-tabs-mode nil)
(setq ring-bell-function 'ignore)
(tool-bar-mode -1)
(fset 'yes-or-no-p 'y-or-n-p)Company
(use-package company
:ensure t
:config
(add-hook 'prog-mode-hook 'global-company-mode)
(setq company-tooltip-align-annotations t))Color Theme
Solarize everything!
(use-package zenburn-theme
:ensure t
:config)Evil
Evil complements the emacs ecosystem perfectly. The first thing I do is get this set up! It’s super important to have the leader keymap as well, so that I can perform all my combinations super fast. A lot of these keybindings have been heavily influenced from spacemacs. I like to throw in magit support and surround support too.
(use-package evil
:ensure t
:diminish evil-mode
:init
(setq evil-want-C-u-scroll t)
:config
(evil-mode 1))
(use-package evil-leader
:ensure t
:config
(evil-leader/set-leader "SPC")
(evil-leader/set-key
"SPC" 'counsel-M-x
"[" 'previous-error
"]" 'next-error
"bb" 'ivy-switch-buffer
"bl" 'dired
"bd" 'kill-buffer
"bk" 'kill-this-buffer
"bD" 'kill-other-buffers
"bn" 'next-buffer
"bp" 'previous-buffer
"eb" 'eval-buffer
"er" 'eval-region
"fs" 'save-buffer
"fo" 'open-org-file
"fc" 'open-calendar-file
"ff" 'counsel-find-file
"fr" 'counsel-recentf
"fed" 'open-config-file
"feR" 'reload-config-file
"sg" 'counsel-git-grep
"tl" 'toggle-truncate-lines
"wc" 'evil-window-delete
"ww" 'ace-window
"wo" 'delete-other-windows
"wj" 'evil-window-down
"wk" 'evil-window-up
"wh" 'evil-window-left
"wl" 'evil-window-right
"wv" 'evil-window-vsplit
"ws" 'evil-window-split)
(global-evil-leader-mode))
(use-package evil-surround
:ensure t
:config
(global-evil-surround-mode))Expand Region
(use-package expand-region
:ensure t
:bind ("C-=" . er/expand-region))FlyCheck
(use-package flycheck
:ensure t
:diminish flycheck-mode
:config
(add-hook 'prog-mode-hook 'flycheck-mode)
(setq-default flycheck-disabled-checker 'javascript-jshint)
(setq-default flycheck-disabled-checker 'json-jsonlist)
(setq-default flycheck-disabled-checker 'javascript-eslint)
(setq-default flycheck-javascript-eslint-executable "eslint-project-relative")
(flycheck-add-mode 'javascript-eslint 'web-mode)
(defun my/use-eslint-from-node-modules ()
(let* ((root (locate-dominating-file
(or (buffer-file-name) default-directory)
"node_modules"))
(eslint (and root
(expand-file-name "node_modules/eslint/bin/eslint.js"
root))))
(when (and eslint (file-executable-p eslint))
(setq-local flycheck-javascript-eslint-executable eslint))))
(add-hook 'flycheck-mode-hook #'my/use-eslint-from-node-modules)
;;; (with-eval-after-load 'flycheck
;;; (advice-add 'flycheck-eslint-config-exists-p :override (lambda() t)))
)General
(setq-default indent-tabs-mode nil)
(global-set-key (kbd "<f5>") 'revert-buffer)
(global-set-key (kbd "<f12>") 'ansi-term)
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
(global-set-key (kbd "s-p") 'counsel-M-x)
(add-hook 'dired-mode-hook
(lambda ()
(define-key dired-mode-map (kbd "^")
(lambda () (interactive) (find-alternate-file "..")))
; was dired-up-directory
))Helper Functions
;; OSX fix for eslint lookup
(use-package exec-path-from-shell
:ensure t
:config
(when (memq window-system '(mac ns))
(exec-path-from-shell-initialize)))
(defun init-install()
(setq use-package-always-ensure t)
reload-config-file())
(defun neotree-find-project-root()
(interactive)
(if (neo-global--window-exists-p)
(neotree-hide)
(let ((origin-buffer-file-name (buffer-file-name)))
(neotree-find (projectile-project-root))
(neotree-find origin-buffer-file-name))))
(defun reload-config-file()
(interactive)
(org-babel-load-file "~/.emacs.d/org-init.org"))
(defun open-config-file()
(interactive)
(find-file "~/.emacs.d/org-init.org"))
(defun open-org-file()
(interactive)
(find-file "~/Dropbox/notes/gtd.org"))
(defun open-calendar-file()
(interactive)
(find-file "~/Dropbox/notes/calendar.org"))
(defun kill-other-buffers (&optional arg)
"Kill all other buffers.
If the universal prefix argument is used then will the windows too."
(interactive "P")
(when (yes-or-no-p (format "Killing all buffers except \"%s\"? "
(buffer-name)))
(mapc 'kill-buffer (delq (current-buffer) (buffer-list)))
(when (equal '(4) arg) (delete-other-windows))
(message "Buffers deleted!")))
Ivy/Swiper
(use-package counsel
:ensure t
:diminish ivy-mode
:config
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-re-builders-alist
'((t . ivy--regex-ignore-order)))
:bind
("s-f" . swiper))Code
Our favourite languages!
C#
(use-package omnisharp
:ensure t
:config
(add-hook 'csharp-mode-hook 'omnisharp-mode))Javascript
Everybody uses JSON now, this make things look very pretty
(use-package json-mode
:ensure t)NPM mode lets us run our npm scripts directly from emacs
(use-package npm-mode
:ensure t)
;; (use-package js2-mode
;; :ensure t
;; :diminish js2-mode
;; :config
;; (setq js2-basic-offset 2)
;; (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
;; (evil-leader/set-key-for-mode 'js2-mode
;; "mf" 'tide-jump-to-definition
;; "mb" 'tide-jump-back)
;;)RJSX mode inherits js2-mode and supports jsx well. In my experience it’s better than js2-jsx-mode, so I use it instead. We set basic offsets and also unmap vim C-d in insert mode, so that we can use the nice tag completion We also integrate with Tide here, so that we can jump back and forth across definitions
(use-package rjsx-mode
:ensure t
:config
(evil-leader/set-key-for-mode 'rjsx-mode
"mf" 'tide-jump-to-definition
"mb" 'tide-jump-back)
(setq js2-basic-offset 2)
(define-key evil-insert-state-map (kbd "C-d") nil)
(add-to-list 'auto-mode-alist '("\\.js\\'" . rjsx-mode))
)Bread and butter web-mode. Highlighting for all things html/css
(use-package web-mode
:ensure t
:config
(defun my-web-mode-hook ()
"Hooks for Web mode. Adjust indents"
(setq web-mode-markup-indent-offset 2)
(setq web-mode-attr-indent-offset 2)
(setq web-mode-css-indent-offset 2)
(setq web-mode-code-indent-offset 2)
(setq css-indent-offset 2))
(add-hook 'web-mode-hook 'my-web-mode-hook))Tide mode utilises Microsoft’s excellent typescript tooling. Tide mode provides excellent code completion, formatting and syntax checking.
(use-package tide
:ensure t
:config
(defun setup-tide-mode ()
(interactive)
(tide-setup)
(flycheck-mode +1)
(setq flycheck-check-syntax-automatically '(save mode-enabled))
(eldoc-mode +1)
(tide-hl-identifier-mode +1)
(tide-setup)
(tide-hl-identifier-mode +1)
)
(add-hook 'js2-mode-hook #'setup-tide-mode)
)Rust
(use-package rust-mode
:ensure t
:mode ("\\.rs\\'" . rust-mode))Let flycheck hook into rust tooling
(use-package flycheck-rust
:ensure t
:config
(add-hook 'flycheck-mode-hook #'flycheck-rust-setup))Autocompletion for rust. I love how new languages provide tooling like this that are editor agnostic.
(use-package racer
:ensure t
:config
(evil-define-key 'insert rust-mode-map
(kbd "TAB") 'company-indent-or-complete-common)
(add-hook 'rust-mode-hook #'racer-mode)
(add-hook 'racer-mode-hook #'eldoc-mode))Cargo lets us build, test and run our rust applications from within emacs. Super useful when developing
(use-package cargo
:ensure t
:config
(add-hook 'rust-mode-hook 'cargo-minor-mode)
(evil-leader/set-key-for-mode 'rust-mode
"mb" 'cargo-process-build
"mr" 'cargo-process-run
"mt" 'cargo-process-test))CSS
(setq css-indent-offset 2)Yaml
Let’s get all our yamls in order
(use-package yaml-mode
:ensure t
:mode ("\\.yml\\'" . yaml-mode)
)Magit
Magit is quite magical. I’m a huge fan of shelling out to command line when possible, but magit is a lot more intuitive, helpful and efficient. Combined with evil-magit and this is my favourite way of doing version control.
(use-package magit
:ensure t
:commands magit-status
:init
(use-package evil-magit
:ensure t)
(evil-leader/set-key
"gs" 'magit-status))Markdown
I try to use org files where possible, but markdown is super useful sometimes for projects. You can install live export tools as well, but I tend not to.
(use-package markdown-mode
:ensure t
:commands (markdown-mode))NeoTree
We need an evil tree! Coupled with some major mode evil bindings and we’re in action
(use-package neotree
:ensure t
:config
(evil-define-key 'normal neotree-mode-map
(kbd "TAB") 'neotree-enter
"H" 'neotree-hidden-file-toggle
"q" 'neotree-hide
(kbd "RET") 'neotree-enter)
(evil-leader/set-key-for-mode 'neotree-mode
"mo" 'neotree-open-file-in-system-application
"md" 'neotree-delete-node
"mr" 'neotree-rename-node
"mc" 'neotree-create-node)
(setq neo-theme 'nerd)
(setq neo-window-fixed-size nil)
(setq neo-smart-open t))
(setq neo-window-width 40)
(setq neo-default-system-application "open")Org
Org mode is an extremely productive way of organising your text files. I have org mode setup in basically a few files:
- GTD.org
- Calendar.org
We use org-capture to easily capture events, ideas and todo items without context switching from what I’m doing.
I also use gcal.el to organise and synchronise with my google calendar. I generally will create an event in google calendar, or from within emacs (and sync).
Then I’ll create a link from my calendar.org file to my gtd.org file with a TODO item against it and the schedule.
A better way might be to just use org-agenda and use the calendar file as well, but I’ll probably experiment with it a little before doing that.
(require 'org-agenda)
(define-key org-agenda-mode-map "c" 'org-agenda-columns)
(setq org-directory "~/Dropbox/notes")
(setq org-default-notes-file (concat org-directory "/gtd.org"))
(define-key global-map "\C-cc" 'org-capture)
(setq org-global-properties '(("Effort_ALL". "0 0:10 0:20 0:30 1:00 2:00 3:00 4:00 6:00 8:00")))
(setq org-columns-default-format '"%25ITEM %10Effort(Est){+} %TODO %TAGS")
(org-agenda-files '"~/Dropbox/notes/gtd.org")
(setq org-tag-alist '((:startgroup . nil)
(:endgroup . nil)
("WORK" . ?w) ("HOME" . ?h) ("COMPUTER" . ?l) ("GOALS" . ?g) ("READING" . ?r) ("PROJECT" . ?p)))
(setq org-agenda-custom-commands
'(("g" . "GTD contexts")
("gw" "Work" tags-todo "WORK")
("gc" "Computer" tags-todo "COMPUTER")
("gg" "Goals" tags-todo "GOALS")
("gh" "Home" tags-todo "HOME")
("gt" "Tasks" tags-todo "TASKS")
("G" "GTD Block Agenda"
((tags-todo "WORK")
(tags-todo "COMPUTER")
(tags-todo "GOALS")
(tags-todo "TASKS"))
nil ;; i.e., no local settings
)))
(evil-leader/set-key
"oc" 'org-capture
"oa" 'org-agenda
"os" 'org-schedule)
(evil-leader/set-key-for-mode 'org-mode
"mt" 'org-set-tags-command
"md" 'org-deadline
"me" 'org-set-effort
"mn" 'org-narrow-to-subtree
"mr" 'org-refile
"mss" 'org-store-link
"msp" 'org-insert-last-stored-link
"mw" 'widen)
(evil-define-key 'normal org-mode-map
">" 'org-shiftmetaright
"<" 'org-shiftmetaleft
"c" 'org-toggle-checkbox
"t" 'org-todo
"gs" 'org-goto)
(evil-leader/set-key-for-mode 'org-capture-mode
"c" 'org-capture-finalize
"k" 'org-capture-kill)
(setq org-capture-templates
'(("t" "Todo" entry (file+headline "~/Dropbox/notes/gtd.org" "Inbox")
"* TODO %?\n%T" :prepend T)
("e" "Event" entry (file "~/Dropbox/notes/calendar.org")
"* %?\n%T" :prepend T)
("i" "Ideas" entry (file+headline "~/Dropbox/notes/gtd.org" "Ideas")
"* %?\n%T" :prepend T)
("g" "Goals" entry (file+headline "~/Dropbox/notes/gtd.org" "Goals")
"* %?\n%T" :prepend T)
("j" "Journal" entry (file+datetree "~/Dropbox/notes/journal.org")
"* %?\nEntered on %U\n %i\n %a")))Just give me nice bullet points!
(use-package org-bullets
:ensure t
:config
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))Create speed commands for editing org files
(setq org-use-speed-commands t)Setup google calendar sync. I keep a secrets file in my Dropbox that I load here as well. Secrets file contains a few variables for secrets and client tokens
(use-package org-gcal
:ensure t
:config
(load-file "~/Dropbox/Keys/gcal.el")
(setq org-gcal-client-id my/google-secrets-client
org-gcal-client-secret my/google-secrets-secret
org-gcal-file-alist '(("clint.ryan3@gmail.com" . "~/Dropbox/notes/calendar.org")))
)Projectile
Projectile is awesome for searching and handling projects.
I ignore node_modules naturally and also have some evil bindings for easily accessing projects using leader keys
(use-package projectile
:ensure t
:diminish projectile-mode
:commands (projectile-find-file projectile-switch-project)
:init
(evil-leader/set-key
"pf" 'projectile-find-file
"pp" 'projectile-switch-project
"pb" 'projectile-switch-buffer
"ft" 'neotree-toggle
"pt" 'neotree-find-project-root)
:config
(setq projectile-completion-system 'ivy)
(add-to-list 'projectile-globally-ignored-directories "node_modules")
(projectile-global-mode))Smart Parenthesis
Hightlight parens smartly :P
(use-package smartparens
:ensure t)Snippets
YaSnippet allows us to insert snippets easily. We disable the <TAB> completion because we use that for other things, but we can insert snippets still using leader bindings.
(use-package yasnippet
:ensure t
:diminish yas-minor-mode
:config
(define-key yas-minor-mode-map (kbd "<tab>") nil)
(define-key yas-minor-mode-map (kbd "TAB") nil)
(evil-leader/set-key
"is" 'yas-insert-snippet
"in" 'yas-new-snippet)
(yas-global-mode 1))Which Key
Awesome package for key discovery!
(use-package which-key
:ensure t
:config
(which-key-mode))