emacs.org 18 KB

emacs/init

This =org= file is evaluated in Emacs by bootstrap.org in init.el. If you’re going to use my dotfiles as an example for how to write your own emacs configuration, please checkout bootstrap.org first.

The purpose of this file is to load all of the other org files.

On 2016-12-09, I started my journey into Emacs. There was no way of knowing that this 6 year running champion for vim would fall deeply into the blackhole that is Emacs, but now that I’m here, there’s no turning back…Emacs is simply the tool I’ve been looking for my entire career as a software engineer.

This document is a living, breathing self-documented configuration. What you see in this file is exactly what my configuration looks like, the init.el will always contain the code found in this document because Emacs org-mode is doing the generation for me.

Setup

caio-load-org-dotfiles is the same function that’s been explained in bootstrap.org. It’s “cheaper” than using org-babel-load-file:

(defun caio-load-org-dotfiles (path)
  (with-temp-buffer
    (insert-file (caio-dotfiles-join path))
    (goto-char (point-min))
    (while (not (eobp))
      (forward-line 1)
      (cond
       ((looking-at "^#\\+BEGIN_SRC +emacs-lisp *$")
        (let ((l (match-end 0)))
          (search-forward "\n#+END_SRC")
          (eval-region l (match-beginning 0)))))
      )))

dotfiles-dir makes a file call for dotfiles easier by getting the path for us:

(setq dropbox-dir "~/Dropbox/org/")
(setq dotfiles-dir "~/src/gitlab.com/chaseadamsio/dotfiles/")
(setq emacs-dir (concat dotfiles-dir "emacs/"))

caio-dotfiles-join takes a path and returns a path for dotfiles/emacs/**/*.org

(defun caio-dotfiles-join (path)
  "Join the dotfile directory with the path provided"
  (concat dotfiles-dir path ".org"))

caio-dropbox-join takes a path and returns a path for Dropbox/org/**/*.org

(defun caio-dropbox-join (path)
  "Join the dotfile directory with the path provided"
  (concat dropbox-dir path ".org"))

Package Management & Package Defaults

Package Management is really important to me, so I did my research and found that Cask meets my requirements:

  • a single file with individually declared dependencies
  • pallet keeps it up to date from running package-install
  • both of these make it easy to reproduce environments across machines
(require 'cask "~/.cask/cask.el")
(cask-initialize)
(require 'pallet)
(pallet-mode t)

Use go-eldoc

(require 'go-eldoc)

Use company

(require 'company)

(setq company-tooltip-limit 20)
(setq company-idle-delay .3)   
(setq company-echo-delay 0)    
(setq company-begin-commands '(self-insert-command))
                                  

Use multicursor…

(require 'multiple-cursors)
(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)

Initialize exec-path-from-shell for getting GOPATH later…

(when (memq window-system '(mac ns))
  (exec-path-from-shell-initialize))

use helm-projectile…

(require 'helm-projectile)
(helm-projectile-on)

Override default emacs buffer display with helm’s buffer list…

(global-set-key (kbd "C-x b") 'helm-buffers-list)

Override default emacs bookmarks with helm’s bookmarks…

(global-set-key (kbd "C-x r b") 'helm-bookmarks)

Override default emacs M-x with helm’s M-x…

(global-set-key (kbd "M-x") 'helm-M-x)

Show kill ring with helm…

(global-set-key (kbd "M-y") 'helm-show-kill-ring)

Override default emacs find-file with helm’s find-file…

(global-set-key (kbd "C-x C-f") 'helm-find-files)

Save backups and auto-saves to system temporary directory rather than in the source directory (version control becomes a nightmare otherwise)

(setq backup-directory-alist
         `((".*" . ,temporary-file-directory)))
   (setq auto-save-file-name-transforms
         `((".*" ,temporary-file-directory t)))

Sensible Resets

Reset yes-or-no to be y-or-n because I hate typing more than I have to:

(defalias 'yes-or-no-p 'y-or-n-p)

Don’t wrap lines for code, but do wrap lines for text…

(add-hook 'prog-mode-hook '(lambda ()
    (setq truncate-lines t
          word-wrap nil)))

(add-hook 'text-mode-hook '(lambda ()
    (setq truncate-lines nil
          word-wrap t)))

make it evil. Enable vim mode and enable evil mode for org mode.

(require 'evil)
(evil-mode 1)
(require 'evil-org)

Reset (C,M)-v to only move half pages forwards and backwards respectively…

(defun scroll-half-page-backward ()
  "scroll down half the page"
  (interactive)
  (scroll-down (/ (window-body-height) 2)))

(defun scroll-half-page-forward ()
  "scroll up half the page"
  (interactive)
  (scroll-up (/ (window-body-height) 2)))

(global-set-key (kbd "C-v") 'scroll-half-page-forward)
(global-set-key (kbd "M-v") 'scroll-half-page-backward)

Use super (cmd) + an arrow to easily navigate windows…

(use-package windmove
  ;; :defer 4
  :ensure t
  :config
  ;; use command key on Mac
  (windmove-default-keybindings 'super)
  ;; wrap around at edges
  (setq windmove-wrap-around t))

Essentials

Automatically insert the closing character for pair-type characters.

(add-hook 'prog-mode-hook 'electric-pair-local-mode)

In some languages (looking at you lisp family!) it can be really hard to tell which pair of parens goes together, enable parens highlighting:

(show-paren-mode 1)

Appearance

I’m using Doom Themes as my color scheme with dark one as the loaded theme.

(require 'doom-themes)
(load-theme 'doom-one t)

Set the doom-buffers to be “brighter”.

(add-hook 'find-file-hook 'doom-buffer-mode)

Set some variables for the doom ui.

(defvar doom-ui-theme 'doom-one
  "The color theme currently in use.")
(defvar doom-ui-font
  (font-spec :family "Fira Mono" :size 9)
  "The font currently in use.")
(defvar doom-ui-variable-pitch-font
  (font-spec :family "Fira Sans" :size 9)
  "The font currently in use.")
(setq org-fontify-whole-heading-line t
      org-fontify-done-headline t
      org-fontify-quote-and-verse-blocks t)

Use doom neotree with nice icons.

(require 'doom-neotree)
(setq doom-neotree-enable-file-icons 'simple)

all-the-icons for beautiful icons in modeline and neotree.

(require 'all-the-icons)

this unclutters the modeline when there are lots of major and minor modes that don’t need to be shown there.

(require 'diminish)
(eval-after-load "yasnippet" '(diminish 'yas-minor-mode))
(eval-after-load "company" '(diminish 'company-mode))
(eval-after-load "eldoc" '(diminish 'eldoc-mode))
(eval-after-load "paredit" '(diminish 'paredit-mode))
(eval-after-load "tagedit" '(diminish 'tagedit-mode))
(eval-after-load "elisp-slime-nav" '(diminish 'elisp-slime-nav-mode))
(eval-after-load "skewer-mode" '(diminish 'skewer-mode))
(eval-after-load "skewer-css" '(diminish 'skewer-css-mode))
(eval-after-load "skewer-html" '(diminish 'skewer-html-mode))
(eval-after-load "smartparens" '(diminish 'smartparens-mode))
(eval-after-load "guide-key" '(diminish 'guide-key-mode))
(eval-after-load "whitespace-cleanup-mode" '(diminish 'whitespace-cleanup-mode))
(eval-after-load "subword" '(diminish 'subword-mode))

murdered out (makes the appearance a lot more subtle on interactions)

(setq-default
 mode-line-default-help-echo nil ; don't say anything on mode-line mouseover
 indicate-buffer-boundaries nil  ; don't show where buffer starts/ends
 indicate-empty-lines nil        ; don't show empty lines
 fringes-outside-margins t       ; switches order of fringe and margin
 ;; Keep cursors and highlights in current window only
 cursor-in-non-selected-windows nil
 highlight-nonselected-windows nil
 ;; Disable bidirectional text support for slight performance bonus
 bidi-display-reordering nil
 blink-matching-paren nil ; don't blink--too distracting
 )

line numers…

(require 'nlinum)
(add-hook 'prog-mode-hook 'nlinum-mode)

Highlight the current line:

(global-hl-line-mode 1)

Configuration File

This defines the order of how the separate code blocks are loaded.

(defvar outline-minor-mode-prefix "\M-#")
<<package-management>>
<<resets>>
<<essentials>>
<<appearance>>
<<funcs-and-macros>>
<<languages>>

Utility Functions & Macros

setup a global key binding for C-x C-r to evaluate the .emacs.d/init.el file (this comes in handy because the init.org is evaluated by this file, so it’s a really easy way to reload configuration without having to open the buffer and evaluate it).

(global-set-key (kbd "C-x C-r") (lambda ()
                                  (interactive)
                                  (load-file "~/.emacs.d/init.el")))

a function for RFC 3339 format (Hugo blog frontmatter)

(defun insert-current-date () (interactive)
    (insert (shell-command-to-string "echo -n $(date +%Y-%m-%dT%H:%M:%SZ)")))

Ido

enable ido mode with flexible matching in both buffer and file search…

;  (setq ido-enable-flex-matching t)
;  (ido-mode 1)

Golang

Import GOPATH from the shell.

(exec-path-from-shell-copy-env "GOPATH")
  • add a hook for go-mode to load
  • use goimports for gofmt-cmd
  • gofmt before save
  • custom compile command
  • godef jump bindings
(defun caio-go-mode-hook ()
  (setq gofmt-command "goimports")
  (add-hook 'before-save-hook 'gofmt-before-save)
  (if (not (string-match "go" compile-command))
      (set (make-local-variable 'compile-command)
           "go build -v && go test -v && go vet"))
  (global-set-key (kbd "M-.") 'godef-describe)
  (global-set-key (kbd "M-*") 'pop-tag-mark)
)
(add-hook 'go-mode-hook 'caio-go-mode-hook)

Use go-eldoc

(add-hook 'go-mode-hook 'go-eldoc-setup)

Use company-go

(require 'company-go)
(add-hook 'go-mode-hook
      (lambda ()
        (set (make-local-variable 'company-backends) '(company-go))
        (company-mode)))

Org

Enable all potential languages that would be used for org-babel

(org-babel-do-load-languages
 'org-babel-load-languages
 '((js . t)
   (sass . t)
   (css . t)
   (sh . t)
   ))

log todos and notes with the time that they were completed:

(setq org-log-done 'time)
(setq org-closed-keep-when-no-todo t)

set files for the org-agenda to use…

(setq org-agenda-files
      (delq nil
            (mapcar (lambda (x) (and (file-exists-p x) x))
                    '("~/notes/work.org"
                      "~/Dropbox/org/organize.org"))))t
(add-to-list 'auto-mode-alist '("\\.txt$" . org-mode))

This matches the todo keywords that I use on a daily basis…

(setq org-todo-keywords
      '((sequence "TODO(t)" "NEXT(n)" "IN PROGRESS(p)" "IN REVIEW(r)" "WAITING(w)" "SOMEDAY(s)" "|" "DONE(d)" "DELEGATED(l)" "CANCELLED(c)")))

activate org-agenda keybinding:

(global-set-key "\C-ca" 'org-agenda)

activate org-capture keybinding:

(global-set-key "\C-cc" 'org-capture)

create a custom method to find a heading under the datree for current date

(defun org-find-heading-in-datetree ()
  (org-datetree-find-date-create (calendar-current-date))
  (goto-char (point-at-eol))
  (when (not (re-search-forward
              (format org-complex-heading-regexp-format
                      (regexp-quote "Things I Read/Watched")) nil t))
    (insert "\n**** Things I Read/Watched\n"))
  (goto-char (point-at-eol)))
(setq org-capture-templates
      '(
        ("p" "Personal Templates")
        ("w" "Work Templates")
        ("pl" "Link" entry (file+function "~/Dropbox/org/journal.org" org-find-heading-in-datetree)
         "* %?\n:PROPERTIES:\n:Url:%i\n:END:\n\n%i")
        ("pt" "TODO Task" entry (file+headline "~/Dropbox/org/inbox.org" "Tasks")
         "* TODO %?\n:LOGBOOK:\n:CREATED: %u\nSRC: %a\n:END:\n%i\n")
        ("wi" "JIRA ISSUE Task" entry (file+headline "~/Notes/work.org" "Backlog")
         "* TODO BFDTORBIT-%^{Issue} %^{Description}\n:PROPERTIES:\n:Link:%i\n:END:\nCREATED: %u\n ")
        )  
      )

set exports to be silent by default for code block executions

(setq org-babel-default-header-args
      (cons '(:results . "none")
      (assq-delete-all :results org-babel-default-header-args)))

a fix for an issue I’m experiencing with doom themes where org-level-1 font has unexpected behavior when moving cursor through characters:

(custom-set-faces
  '(org-level-1 ((t (:line-width 1))))
)

Make org-level-1 the same height as other lines (the doom themes sizing makes the headlines do wonky things with my font)

Org Publish

(require 'ox-publish)
(setq caio-header-file "~/src/gitlab.com/chaseadamsio/dotfiles/partials/header.html")
(defun caio-header (arg)
  (with-temp-buffer
    (insert-file-contents caio-header-file)
    (buffer-string)))

(setq org-publish-project-alist
      '(
        ("dotfiles"
         :base-directory "~/src/gitlab.com/chaseadamsio/dotfiles/"
         :base-extension "org"
         :publishing-directory "~/src/gitlab.com/chaseadamsio/dotfiles/public_html/"
         :section-numbers nil
         :recursive t
         :publishing-function org-html-publish-to-html
         :headline-level 4
         :html-head "<link href=\"https://chaseadams.io/css/main.css\" rel=\"stylesheet\">"
         :html-doctype "html5"
         :html-divs caio-org-html-divs
         :html-preamble caio-header
         :with-toc nil)
      ("images"
       :base-directory "~/src/gitlab.com/chaseadamsio/dotfiles/"
       :base-extension "jpg\\|gif\\|png"
       :publishing-directory "~/src/gitlab.com/chaseadamsio/dotfiles/public_html"
       :publishing-function org-publish-attachment)
      ("dotfiles-all" :components ("dotfiles" "images"))))

Tramp

(setq tramp-default-method "ssh")

Appearance (refactor)

Disable the audible bell (I’m smart, I can figure out when I’ve done something wrong) and disable all the chrome for the emacs external application.

(setq ring-bell-function 'ignore) ;; the bell annoys the h*ck out of me, turn it off
(setq initial-scratch-message "")
(setq inhibit-startup-message t)

(if (fboundp 'menu-bar-mode) (menu-bar-mode -1)) ;; for a "thin" emacs app
(if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1)) ;; for a "thin" emacs app
(if (fboundp 'tool-bar-mode) (tool-bar-mode -1)) ;; for a "thin" emacs app

Use Fira Mono, set the line height and shrink the font to 9pt.

(set-face-attribute 'default nil :family "Fira Mono")
(setq-default line-spacing 3)
(set-face-attribute 'default nil :height 100)

I like my cursor to be a thin line rather than a chunky block. I’ve always found the chunky block to be distracting.

(setq-default cursor-type 'bar)

One of the things I dislike about emacs is when you select some text and want to replace it with other text the way you would in a sane editor, it leaves the selected text and places the cursor at the end of the selection and starts typing. This “fixes” that so that when I select text and start to type, it replaces the selected text with my newly typed words.

(delete-selection-mode 1)

Registers (refactor)

Registers are a convenient way to access frequently opened files in a quick manner.

You can access a register by typing C-x r j followed by the letter in the set-register ? to jump to that file.

Organize Registers

Quickly open my Dropbox journal:

(set-register ?j (cons 'file (caio-dropbox-join "journal")))

Quickly open my Dropbox organize:

(set-register ?o (cons 'file (caio-dropbox-join "organize")))

Dotfiles Registers

Register for opening [[file:init.org][init.org]]

(set-register ?e (cons 'file (caio-dotfiles-join "emacs")))

register for opening dotfiles

(set-register ?d (cons 'file dotfiles-dir))