Jack Baty's Emacs Configuration

Table of Contents

1 Configuration

1.1 About this file

My personal Emacs/Spacemacs configuration.

Most of this was borrowed straight from the ever-helpful Sacha Chua. http://pages.sachachua.com/.emacs.d/Sacha.html

Generate jack.el using C-c C-v t

1.2 Starting up

;; Everything here is generated via ./jack.org so start there
;; My Misc User Config  ------------------------------------------------------------------

;; Where I keep my custom lisp and packages
(add-to-list 'load-path "~/Dropbox/Sync/dotfiles/lisp")

1.3 My Info

(setq user-full-name "Jack Baty")
(setq user-mail-address "jack@fusionary.com")

1.4 Emacs initialization

;; Initial window size
(setq initial-frame-alist '((top . 20) (left . 5) (width . 170) (height . 50)))

1.4.1 Load secrets

Keep sensitive information in a separate file so I don't accidentally publish it

(load "~/.emacs.secrets" t)

1.5 General Configuration

1.5.1 Libraries

(require 'ob-jira-rest)
(require 'org-protocol)

1.5.2 Theme

I don't know why these don't stick unless they're set via Customize

;; (setq spacemacs-theme-org-height nil)
;;(setq spacemacs-theme-org-agenda-height nil)

1.5.3 Backups

This is one of the things people usually want to change right away. By default, Emacs saves backup files in the current directory. These are the files ending in ~ that are cluttering up your directory lists. The following code stashes them all in ~/.emacs.d/backups, where I can find them with C-x C-f (find-file) if I really need to.

(setq backup-directory-alist '(("." . "~/.emacs.backups")))

Disk space is cheap. Save lots.

(setq delete-old-versions -1)
(setq version-control t)
(setq vc-make-backup-files t)
;; (setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t)))


I want bookmarks stored outside of .emacs.d and also synced

(setq bookmark-default-file "~/Dropbox/Sync/dotfiles/emacs-bookmarks")

1.5.5 Reading PDFs

;; Reading a PDF while taking notes in another window (2 window setup)
;; Hit M-[/M-] to go up/down while keeping cursor in current window.
;; http://www.idryman.org/blog/2013/05/20/emacs-and-pdf/
(fset 'doc-prev "\C-xo\C-u\C-xo")
(fset 'doc-next "\C-xo\C-d\C-xo")
(global-set-key (kbd "M-[") 'doc-prev)
(global-set-key (kbd "M-]") 'doc-next)

;; refresh on change
(add-hook 'doc-view-mode-hook 'auto-revert-mode)

(add-hook 'text-mode-hook 'turn-on-auto-fill)

1.6 Buffers and files

(defun rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " filename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

(global-set-key (kbd "C-x C-r") 'rename-current-buffer-file)

1.7 Encryption

;; Encryption ------------------------------------------------------------------------
  (require 'epa)
  (require 'org-crypt)
  ;; GPG key to use for encryption
  ;; Either the Key ID or set to nil to use symmetric encryption.
  (setq org-crypt-key "53B55FAC")
  (setq auto-save-default nil)
;; /Encryption ------------------------------------------------------------------------

1.8 YASnippet

(setq yas-snippet-dirs
      '("~/Dropbox/Sync/snippets"                 ;; personal snippets)
(yas-global-mode 1)

1.9 Ledger

(setq ledger-clear-whole-transactions t)
(setq ledger-highlight-xact-under-point nil)
(setq ledger-use-iso-dates t)

1.10 Mu4e and email

;; Mu4e ------------------------------------------------------------------------
;; Mail Mu4e

(require 'org-mu4e)

(setq mu4e-maildir "~/Mail")

;;store link to message if in header view if nil - header query if t
(setq org-mu4e-link-query-in-headers-mode t)
(setq mu4e-change-filenames-when-moving t)
(setq mu4e-view-prefer-html nil)
(setq mu4e-attachment-dir "~/Desktop")
;;(setq mu4e-use-fancy-chars t) ;;
(setq mu4e-view-show-addresses t) ;; I prefer to see both name and address
(setq mu4e-compose-keep-self-cc nil) ;; Don't include my address on CC when replying
(setq mu4e-compose-dont-reply-to-self t) ;; trying because the above doesn't work
(setq mu4e~headers-sort-direction 'ascending)
(setq mu4e-enable-notifications nil)
(setq mu4e-alert-style (quote osx-notifier))
(setq mu4e-compose-complete-only-personal t)

(require 'mu4e-contrib)
(setq mu4e-html2text-command 'mu4e-shr2text)
;; make shr/eww readable with dark themes
;;(setq shr-color-visible-luminance-min 70)
(add-hook 'mu4e-view-mode-hook
            ;; try to emulate some of the eww key-bindings
            (local-set-key (kbd "<tab>") 'shr-next-link)
            (local-set-key (kbd "<backtab>") 'shr-previous-link)))

(add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t)
;; Try to display images in mu4e
 mu4e-view-show-images t
 mu4e-view-image-max-width 800)

;; don't save message to Sent Messages, Gmail/IMAP takes care of this
(setq mu4e-sent-messages-behavior 'delete)

(setq message-send-mail-function 'smtpmail-send-it)

(setq mu4e-contexts
    `( ,(make-mu4e-context
      :name "work"
      :enter-func (lambda () (mu4e-message "Switch to the Fusionary context"))
    ;; leave-func not defined
    :match-func (lambda (msg)
      (when msg 
        (mu4e-message-contact-field-matches msg 
          :to "@fusionary.com")))
    :vars '(  ( user-mail-address      . "jack@fusionary.com"  )
              ( user-full-name      . "Jack Baty" )
              ( mu4e-maildir-shortcuts .
               (("/Fusionary/INBOX"             . ?i)
                 ("/Fusionary/@SaneLater" . ?l)
              ( mu4e-get-mail-command . "mbsync -Vq inboxes")
              ( mu4e-sent-folder . "/Fusionary/Sent Mail")
              ( mu4e-drafts-folder . "/Fusionary/Drafts")
              ( mu4e-refile-folder . "/Fusionary/Archive")
              ( mu4e-trash-folder . "/Fusionary/trash")
              ( smtpmail-stream-type . starttls  )
              ( smtpmail-default-smtp-server . "smtp.gmail.com"  )
              ( smtpmail-smtp-server . "smtp.gmail.com"  )
              ( smtpmail-smtp-service . 587  )

    :name "personal"
    :enter-func (lambda () (mu4e-message "Switch to the Personal context"))
    ;; leave-fun not defined
    :match-func (lambda (msg)
      (when msg 
        (mu4e-message-contact-field-matches msg 
          :to "@baty.net")))
    :vars '(  ( user-mail-address      . "jack@baty.net" )
              ( user-full-name      . "Jack Baty" )
              ( mu4e-maildir-shortcuts .
                                       (("/Personal/INBOX"             . ?i)
                                        ("/Personal/SaneLater" . ?l)
              ( mu4e-sent-folder . "/Personal/Sent")
              ( mu4e-drafts-folder . "/Personal/Drafts")
              ( mu4e-refile-folder . "/Personal/Archive")
              ( mu4e-trash-folder . "/Personal/trash")
              ( mu4e-get-mail-command . "mbsync -Vq inboxes")
              ( message-send-mail-function . smtpmail-send-it)
              ( smtpmail-stream-type . starttls)
              ( smtpmail-default-smtp-server . "mail.messagingengine.com")
              ( smtpmail-smtp-server . "mail.messagingengine.com")


;;; Bookmarks
(setq mu4e-bookmarks
      `(("flag:unread AND NOT flag:trashed" "Unread messages" ?u)
        ("date:today..now" "Today's messages" ?t)
        ("maildir:\"/Fusionary/Sent Mail\" date:1d..now"       "Sent recently"     ?r)
        ("flag:flagged"       "Flagged messages"     ?f)
        ("date:7d..now" "Last 7 days" ?w)
        ("mime:image/*" "Messages with images" ?p)
        (,(mapconcat 'identity
                      (lambda (maildir)
                        (concat "maildir:" (car maildir)))
                      mu4e-maildir-shortcuts) " OR ")
         "All inboxes" ?i)))

;;(add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display)

(defun djcb-mu4e-copy-message-at-point (&optional dir)
  "Copy message at point to somewhere else as <date>_<subject>.eml."
  (let* ((msg (mu4e-message-at-point))
         (target (format "%s_%s.eml"
                         (format-time-string "%F" (mu4e-message-field msg :date))
                         (or (mu4e-message-field msg :subject) "No subject"))))
     (mu4e-message-field msg :path)
     (format "%s/%s" (or dir (read-directory-name "Copy message to: ")) target) 1)))

(global-set-key (kbd "<f5>") 'djcb-mu4e-copy-message-at-point)    ; Copy email (move to better spot?)

;; start with the first (default) context; 
;; default is to ask-if-none (ask when there's no context yet, and none match)
(setq mu4e-context-policy 'pick-first)

;; compose with the current context if no context matches;
;; default is to ask 
(setq mu4e-compose-context-policy nil)

;; /Mu4e ------------------------------------------------------------------------

1.11 Publishing

;; Publishing/HTML ---------------------------------------------------------------
  (setq org-html-validation-link nil)
  (setf org-export-html-coding-system 'utf-8-unix)

    (setf org-html-preamble nil)
    (setf org-html-postamble t)
    (setf org-html-postamble-format
   "<p>Author: Jack Baty <a href='mailto:%e' rel='author'>%e</a>.<br>\n"
   "Last update : %C"

(setq org-publish-project-alist
        ("notes" :components ("notes-org" "notes-static"))

         :base-directory "~/Dropbox/Sites/notes-emacs/content/"
         :base-extension "org"
         :publishing-directory "~/Dropbox/Sites/notes-emacs/public_html/"
         :recursive t
         :publishing-function org-html-publish-to-html
         :html-html5-fancy t
         :auto-sitemap t
         :exclude "nav.org"
         :sitemap-title ""
         :html-style nil
         :html-head-include-default-style nil
         :html-head-include-scripts nil

         :base-directory "~/Dropbox/Sites/notes-emacs/content/"
         :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
         :publishing-directory "~/Dropbox/Sites/notes-emacs/public_html/"
         :recursive t
         :publishing-function org-publish-attachment

       ;; Path to your org files.
       :base-directory "~/Dropbox/Sites/notes/org/"
       :base-extension "org"

       ;; Path to your Jekyll project.
       :publishing-directory "~/Dropbox/Sites/notes/jekyll/"
       :recursive t
       :publishing-function org-html-publish-to-html
       :headline-levels 4 
       :html-extension "html"
       :body-only t ;; Only export section between <body> </body>

       :base-directory "~/Dropbox/Sites/notes/org/"
       :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|php"
       :publishing-directory "~/Dropbox/Sites/notes/"
       :recursive t
       :publishing-function org-publish-attachment)

      ("jekyll" :components ("org-jekyll" "org-static-jekyll"))


;; /Publishing ------------------------------------------------------------------------

1.12 Deft

;; Deft ------------------------------------------------------------------------
(global-set-key [f12] 'deft)
(setq deft-directory "~/Dropbox/deft")
(setq deft-extensions '("org" "md" "txt" "taskpaper" "tex"))
(setq deft-use-filter-string-for-filename t)
;; /Deft ------------------------------------------------------------------------

1.13 Org Mode

1.13.1 Modules

  (setq org-modules '(org-bbdb
(eval-after-load 'org
 '(org-load-modules-maybe t))
;; Prepare stuff for org-export-backends
(setq org-export-backends '(org latex icalendar html ascii))

1.13.2 General

  ;; General org-mode configuration

  (setq org-directory "~/Dropbox/org/")
  (setq org-agenda-files (quote ("~/Dropbox/org")))
  (setq org-default-notes-file (quote (concat org-directory "inbox.org")))
  (setq org-refile-targets (quote ((nil :maxlevel . 2)
                                  (org-agenda-files :maxlevel . 2))))

  (setq org-refile-use-outline-path t)
  (setq org-outline-path-complete-in-steps nil)
  (setq org-refile-allow-creating-parent-nodes (quote confirm))
  (setq org-todo-keywords
        '((sequence "TODO(t)" "PEND(p)" "NEXT(n)" "WAIT(w@)" "|" "DONE(d!)" "CANC(@c)")))

  (setq org-agenda-include-diary nil)
  (setq org-icalendar-timezone "America/Detroit")
  (setq org-cycle-separator-lines 0)
  ;; Throw error if you try to delete hidden/folded text
  (setq org-catch-invisible-edits 'error)
  ;; export settings
  (setq org-html-head-include-default-scripts nil)
  (setq org-html-head-include-default-style nil)
  (setq org-agenda-skip-deadline-prewarning-if-scheduled t)
  ;;don't show tasks that are scheduled or have deadlines in the
  ;;normal todo list
  (setq org-agenda-todo-ignore-deadlines (quote all))
  (setq org-agenda-todo-ignore-scheduled (quote all))

  ;; org-babel config
  (setq org-startup-folded "overview")
  (setq org-src-tab-acts-natively t)
  (setq org-confirm-babel-evaluate nil)
  (setq org-ditaa-jar-path "/usr/local/bin/ditaa")

  (setq org-contacts-files '("/Users/jbaty/Dropbox/People/"))

  (setq org-adapt-indentation t)
  (setq org-agenda-insert-diary-strategy (quote date-tree-last))
  (setq org-agenda-log-mode-items (quote (closed)))
  (setq org-agenda-skip-scheduled-if-deadline-is-shown (quote not-today))
  (setq org-agenda-skip-scheduled-if-done t)
  (setq org-agenda-span (quote day))
  (setq org-agenda-text-search-extra-files (quote (agenda-archives)))
  (setq org-agenda-window-setup (quote other-window))

;; active Babel languages
 '((R . t)
   (emacs-lisp . nil)
   (emacs-lisp . t)
   (latex . t)
   (org . t)
   (ruby . t)
   (ledger . t)
   (awk . t)
  (setq org-clock-mode-line-total (quote current))
  (setq org-deadline-warning-days 4)
  (setq org-latex-pdf-process
    ("xelatex -interaction nonstopmode -output-directory %o %f" "xelatex -interaction nonstopmode -output-directory %o %f")))
  (setq org-log-into-drawer t)
  (setq org-refile-targets (quote ((org-agenda-files :maxlevel . 3))))
  (setq org-refile-use-outline-path t)
  (setq org-reverse-note-order t)
  (setq org-startup-indented t)
  (setq org-stuck-projects (quote ("project/-DONE-CANC" ("TODO" "NEXT") nil "")))
  (setq org-use-tag-inheritance nil)

1.13.3 Org Habit

;; org-habit
(require 'org-habit)
(add-to-list 'org-modules "org-habit")
(setq org-habit-preceding-days 7
      org-habit-following-days 1
      org-habit-show-habits-only-for-today t
      org-habit-graph-column 50
      org-habit-show-all-today t)

1.13.4 Clocking

;; Org clock config
;; Don't Resume clocking task when emacs is restarted (takes too long)
;;(org-clock-persistence-insinuate nil)
;; Resume clocking task on clock-in if the clock is open
(setq org-clock-in-resume t)
;; Separate drawers for clocking and logs
;;(setq org-drawers (quote ("PROPERTIES" "LOGBOOK")))
(setq org-clock-into-drawer t)
(setq org-clock-out-remove-zero-time-clocks t)
(setq org-clock-out-when-done t)
;; Save the running clock and all clock history when exiting Emacs, load it on startup
(setq org-clock-persist t)
(setq org-clock-report-include-clocking-task t)
;; Tidy up the clockreport
(setq org-agenda-clockreport-parameter-plist
    (quote (:link t :maxlevel 5 :fileskip0 t :compact t :narrow 60 :formula %)))

1.13.5 Agenda Custom Commands

(setq org-agenda-custom-commands
      (append '(("c" "Waiting for Client" tags-todo "Client")
                ("w" "Waiting" todo "WAIT" nil)
                ("p" "Active Projects" tags "project/-DONE-CANC" nil)
                ("Q" tags "questions")
                ("b" tags "bought")
                ("l" tags "Links")
                ("v" tags-todo "Events"))))

1.13.6 Capture Templates

;; Capture
(setq org-capture-templates
          '(("t" "Todo" entry
             (file+headline "~/org/todo.org" "Inbox")
             "* TODO %? \n %i\n")
            ("T" "Todo and clock in" entry
             (file+headline "~/org/todo.org" "Inbox")
             "* TODO %? \n %i\n"
             :clock-in t
             :clock-keep t)
            ("n" "Take a note")
            ("ns" "Scraps for processing later" entry
             (file+headline "~/org/scraps.org" "Scraps")
             "* %?\n%u")
            ("nt" "Log to Technotes" entry
             (file+headline "~/org/technotes.org" "Technotes")
             "* %?\n%u")
            ("nb" "Notes.baty.net" entry
             (file+headline "~/Dropbox/Sites/notes-emacs/content/index.org" "Latest Notes")
             "* %? %t\n "
             :prepend t)
            ("nk" "Sparkfile entry" item
             (file+headline "~/org/sparkfile.org" "2016")
             "- [ ] %? %u"
             :prepend t)
            ("nf" "Fusionary Notebook entry" entry
             (file+datetree "~/org/fusionary.org")
             "**** %? \n%U"
             :empty-lines 1)
            ("w" "Worklog" entry
             (file+datetree "~/org/worklog.org")
             "* %? %^g\n%T")
            ("W" "Worklog and clock in" entry
             (file+datetree "~/org/worklog.org")
             "* %? %^g\n%T"
             :clock-in t
             :clock-keep nil)
            ("c" "Client log entries")
            ("ch" "HealthcareSource" entry
             (file+datetree "~/org/HCS.org")
             "* %? %^g")
            ("ct" "Trademarx" entry
             (file+datetree "~/org/Trademarx.org")
             "* %? %^g")
            ("cs" "Steelcase" entry
             (file+datetree "~/org/Steelcase.org")
             "* %? %^g")
            ("f" "Food log entries")
            ("fb" "Breakfast entry" entry
             (file+olp "~/org/daybook.org" "Food Log" "December")
             "* %u %? :Breakfast:%^g"
             :prepend nil)
            ("fl" "Lunch entry" entry
             (file+olp "~/org/daybook.org" "Food Log" "December")
             "* %u %? :Lunch:%^g"
             :prepend nil)
            ("fd" "Dinner entry" entry
             (file+olp "~/org/daybook.org" "Food Log" "December")
             "* %u %? :Dinner:%^g"
             :prepend nil)
            ("d" "Daybook" entry
             (file+olp "~/org/daybook.org" "Daybook" "December")
             "* %u %? %^g")
            ("m" "TODO from Mail" entry
             (file+headline "~/org/todo.org" "Inbox")
             "* TODO Followup%? (%:fromname about %:subject)\n%U\n%a\n")
            ("b" "Bookmark links" entry
             (file+headline "~/org/links.org" "Bookmarks")
             "* %? %^g\nEntered on: %U\n%c")
(with-eval-after-load 'org-capture
    (add-hook 'org-capture-mode-hook 'evil-insert-state))

1.13.7 Opening specific files

;; open fusionary.org  with Spc of
(defun my/open-fusionary-notebook ()
  "Open fusionary.org"
  (find-file "~/Dropbox/org/fusionary.org"))

(evil-leader/set-key "of" 'my/open-fusionary-notebook)

;; Open todos with Spc ot
(defun my/open-todo ()
  "Open Todo file"
  (find-file "~/Dropbox/org/todo.org"))

(evil-leader/set-key "ot" 'my/open-todo)

;; Open daybook with Spc od
(defun my/open-daybook ()
  "Open Daybook file"
  (find-file "~/Dropbox/org/daybook.org"))

(evil-leader/set-key "od" 'my/open-daybook)

1.13.8 URL Handler for macOS links

;; Open special url-handler links with macOS `open' command
;; This means you can open DevonThink and Papers links in org files
(defun jab-org/org-pass-link-to-system (link)
  (if (string-match "^[a-zA-Z0-9\-]+:" link)
      (shell-command (concat "open " (shell-quote-argument link)))

(add-hook 'org-open-link-functions 'jab-org/org-pass-link-to-system)

1.13.9 LaTeX

;; LaTeX

;; My default LaTeX class
(with-eval-after-load 'ox-latex
  (add-to-list 'org-latex-classes
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\paragraph{%s}" . "\\paragraph*{%s}")
               ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))

(setq org-latex-caption-above nil)

1.13.10 Counting tag frequency

;; For counting tag frequency
;; http://emacs.stackexchange.com/questions/29127/make-statistics-of-the-tag-use-in-org-mode#29133

(defun org-freq-count (search targets &optional cmp)
  (let ((cmp (if (functionp cmp)
               (lambda (a b) nil))))

    (mapcar (lambda (x)
              (list x (length (org-map-entries t (format search x) nil))))
              (-filter #'stringp targets))

(defun org--tagblock-all-tags ()
  (-filter #'stringp (-map #'car (append

(defun org-write-freq-count (search targets name)
  (insert (s-concat
           (if name (insert (format "#+NAME: %s\n" name)))
            (lambda (x) (format "| %s | %s |" (nth 0 x) (nth 1 x)))
            (org-freq-count search targets)

(defun org-dblock-write:tagblock (params)
  (let ((todo (plist-get params :todo))
        (tags (or (plist-get params :tags) (org--tagblock-all-tags)))
        (label (plist-get params :label))
        (caption (plist-get params :caption))
    (when caption (insert (format "#+CAPTION: %s\n" caption)))
    (org-write-freq-count (cond ((equal todo t)
                                 (format "%%s/%s" (mapconcat 'identity
                                ((listp todo)
                                 (format "%%s/%s" (mapconcat 'identity
                                (t "%s"))

1.13.11 Copy region as HTML

;; from Sacha Chua
;; "Sometimes I want a region's HTML in my kill-ring/clipboard without any of the extra fluff:"
(defun my/org-copy-region-as-html (beg end &optional level)
  "Make it easier to copy code for Wordpress posts and other things."
  (interactive "r\np")
  (let ((org-export-html-preamble nil)
        (org-html-toplevel-hlevel (or level 3)))
     (org-export-string-as (buffer-substring beg end) 'html t))))

2 To be dealt with

Author: Jack Baty jack@baty.net.
Last update : 2016-12-27 Tue 15:03