Permalink
2d4e063 Sep 12, 2018
1 contributor

Users who have contributed to this file

7478 lines (6838 sloc) 267 KB

Emacs Literate Configuration

This is my emacs configuration file. I use org-mode to organize it and then “tangle” the file using org-babel. You can find a helpful discussion of this method here.

I use emacs for all my academic work. The configuration tends in that direction. It also uses vim keybindings. I used vim (or neovim) previously. I think vim’s modal editing is the best way to use a text-editor. If you like vim bindings but don’t tend to want to think about configuring emacs, I strongly recommend spacemacs or doom-emacs as a way of managing your configuration. For a more traditional emacs feel there are also the configurations of purcell and batsov (prelude) and many others. For a minimally sane setup you might also look at some sensible defaults. This file is also influenced by the bmacs configuration of Bryan Gilbert (see also his literate starter kit) and Karl Voit’s config. Use this along with my emacs init file.

Table of Contents

Personal Information

Let’s set some variables with basic user information.

(setq user-full-name "Colin McLear"
      user-mail-address "mclear@fastmail.com")
      

Directory Variables

We’re going to define a number of directories that are used throughout this configuration to store different types of files.

(eval-and-compile
  (defvar cpm-emacs-dir (expand-file-name user-emacs-directory)
    "The path to the emacs.d directory.")

  (defvar cpm-local-dir (concat cpm-emacs-dir ".local/")
    "Root directory for local Emacs files. Use this as permanent
  storage for files that are safe to share across systems (if
  this config is symlinked across several computers).")

  (defvar cpm-host-dir (concat cpm-local-dir "@" (system-name))
    "Directory for hostname-specific file storage. Used by `cpm-etc-dir' and
  `cpm-cache-dir'.")

  (defvar cpm-etc-dir (concat cpm-host-dir "/etc/")
    "Host-namespaced directory for non-volatile storage. These are not deleted or
  tampored with by emacs functions. Use this for dependencies like servers or
  config files that are stable (i.e. it should be unlikely that you need to delete
  them if something goes wrong).")

  (defvar cpm-cache-dir (concat cpm-host-dir "/cache/")
    "Host-namespaced directory for volatile storage. Deleted when `cpm/reset' is
  called. Use this for transient files that are generated on the fly like caches
  and temporary files. Anything that may need to be cleared if there are
  problems.")

  (defvar cpm-elisp-dir (concat cpm-local-dir "/elisp/")
    "Where personal elisp packages and scripts are stored.")


  (dolist (dir (list cpm-local-dir cpm-etc-dir cpm-cache-dir cpm-elisp-dir))
    (unless (file-directory-p dir)
  (make-directory dir t))))

Security

Properly verify outgoing ssl connections.

(setq gnutls-verify-error t
      tls-checktrust gnutls-verify-error
      tls-program (list "gnutls-cli --x509cafile %t -p %p %h"
                        ;; compatibility fallbacks
                        "gnutls-cli -p %p %h"
                        "openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof")
      nsm-settings-file (expand-file-name "network-security.data" cpm-cache-dir))

Emacs Initialization

Garbage Collection

We increase the gc-cons-threshold to a very high number to decrease the load and compile time. We’ll lower this value significantly after initialization has completed. We don’t want to keep this value too high or it will result in long GC pauses during normal usage.

(defun cpm/config-setup-hook ()
  (eval-and-compile
  (setq gc-cons-threshold most-positive-fixnum
        gc-cons-percentage 0.6)))

(defun cpm/config-exit-hook ()
  (setq gc-cons-threshold 80000
      gc-cons-percentage 0.1))

(add-hook 'before-init-hook #'cpm/config-setup-hook)
(add-hook 'after-init-hook  #'cpm/config-exit-hook)

Byte Compilation Warnings

Disable certain byte compiler warnings to cut down on the noise. This is a personal choice and can be removed if you would like to see any and all byte compiler warnings.

(setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))

Package Settings

We’re going to set the load-path ourselves and avoid calling (package-initilize) (for performance reasons) so we need to set package--init-file-ensured to true to tell package.el to not automatically call it on our behalf. Additionally we’re setting package-enable-at-startup to nil so that packages will not automatically be loaded for us since use-package will be handling that.

(eval-and-compile
  (setq load-prefer-newer t
        package-user-dir (concat cpm-local-dir "/elpa/") 
        package--init-file-ensured t
        package-enable-at-startup nil)

  (unless (file-directory-p package-user-dir)
    (make-directory package-user-dir t)))

Use-Package Settings

I used to tell use-package to always defer loading packages unless explicitly told otherwise. This speeds up initialization significantly as many packages are only loaded later when they are explicitly used. But it can also cause problems. I’ve since revised all my defer settings. I explicitly defer/demand, and put a lot of loading of packages off until after about 10 secs of idle. The latter means package loading stays out of my way if I’m doing, e.g., a quick restart-and-check of something in emacs. But I still use always-defer, as it seems to shave another .4 secs off load time.

(setq use-package-always-defer nil
      use-package-verbose t)

Manually Set Load Path

We’re going to set the load path ourselves so that we don’t have to call package-initialize at runtime and incur a large performance hit. This load-path will actually be faster than the one created by package-initialize because it appends the elpa packages to the end of the load path. Otherwise any time a builtin package was required it would have to search all of third party paths first.

(eval-and-compile
  (setq load-path (append load-path (directory-files package-user-dir t "^[^.]" t))))

Initialize Package Management

Next we are going to require package.el and add our additional package archives, ‘melpa’ and ‘org’. Afterwards we need to initialize our packages and then ensure that use-package is installed, which we promptly install if it’s missing. Finally we load use-package and tell it to always install any missing packages.

Note that this entire block is wrapped in eval-when-compile. The effect of this is to perform all of the package initialization during compilation so that when byte compiled, all of this time consuming code is skipped. This can be done because the result of byte compiling use-package statements results in the macro being fully expanded at which point use-package isn’t actually required any longer.

Since the code is automatically compiled during runtime, if the configuration hasn’t already been previously compiled manually then all of the package initialization will still take place at startup.

(eval-when-compile
  (require 'package)

  (unless (assoc-default "melpa" package-archives)
    (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t))
  (unless (assoc-default "gnu" package-archives)
    (add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/") t))
  (unless (assoc-default "org" package-archives)
    (add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t))
  ;; https://github.com/emacs-china/emacswiki-elpa
  (unless (assoc-default "emacswiki" package-archives)
    (add-to-list 'package-archives '("emacswiki" . "https://mirrors.tuna.tsinghua.edu.cn/elpa/emacswiki/") t))


  (package-initialize)
  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package))
  (require 'use-package)
  (setq use-package-always-ensure t))

Paradox Package Management

(use-package paradox
  :commands (paradox-list-packages paradox-upgrade-packages)
  :config
  (add-to-list 'evil-emacs-state-modes 'paradox-menu-mode)
  (setq paradox-execute-asynchronously nil
         ;; Show all possible counts
        paradox-display-download-count t
        paradox-display-star-count t
        ;; Don't star automatically
        paradox-automatically-star nil))

Quelpa

Get emacs packages from anywhere and use with use-package

(use-package quelpa
  :ensure t
  :commands quelpa
  :init
  ;; disable checking Melpa
  (setq quelpa-update-melpa-p nil)
  ;; don't use Melpa at all
  (setq quelpa-checkout-melpa-p nil)
  ;; quelpa dir settings
  (setq quelpa-dir (concat cpm-local-dir "quelpa")))

(use-package quelpa-use-package
  :ensure t
  :defer 5
  :config
  ;; advince for maybe installing with quelpa
  (setq quelpa-use-package-inhibit-loading-quelpa t)
  (quelpa-use-package-activate-advice))

Useful Libraries

async, s, dash, and cl-lib are libraries for asynchronous processing, string manipulation, list manipulation and backward compatibility respectively. The git package is also a library.

(use-package async   :defer 10)
(use-package dash    :defer 10)
(use-package s       :defer 10)
(use-package f       :defer 10)
(use-package subr-x  :defer 10 :ensure nil)
; lots of packages depend on these libraries
(use-package cl-lib  :demand t :ensure nil)
(use-package cl      :demand t :ensure nil)

Functions & Macros

Useful Functions

Various useful functions and macros I’ve written or pilfered from others.

Archive All Done Tasks

Useful function for archiving done tasks. From stack overflow.

(defun cpm/org-archive-done-tasks ()
  (interactive)
  (org-map-entries
   (lambda ()
     (org-archive-subtree)
     (setq org-map-continue-from (outline-previous-heading)))
   "/DONE" 'agenda))

Blank Buffer New Frame

Make a blank buffer when opening a new frame. From https://stackoverflow.com/a/25792276. I added a call to persp-mode since I only ever want new frames to use new perspectives

(defun cpm/new-buffer-new-frame ()
  "Create a new frame with a new empty buffer & turn on persp-mode."
  (interactive)
  (persp-mode 1)
  (let ((buffer (generate-new-buffer "untitled")))
    (set-buffer-major-mode buffer)
    (display-buffer buffer '(display-buffer-pop-up-frame . nil))))

Built-in Functions

These are useful built-in functions, but you have to enable them

(put 'erase-buffer 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(put 'dired-find-alternate-file 'disabled nil)

Call an emacs instance

Call an emacs instance for testing

(defun cpm/call-emacs ()
  (interactive)
  (start-process "Emacs" nil
                 (executable-find "/Applications/Emacs.app/Contents/MacOS/Emacs")))

Clipboard to/from Buffer

;; http://stackoverflow.com/a/10216338/4869
(defun cpm/copy-whole-buffer-to-clipboard ()
  "Copy entire buffer to clipboard"
  (interactive)
  (clipboard-kill-ring-save (point-min) (point-max)))

(defun cpm/copy-clipboard-to-whole-buffer ()
  "Copy clipboard and replace buffer"
  (interactive)
  (delete-region (point-min) (point-max))
  (clipboard-yank)
  (deactivate-mark))

Config functions

Useful functions for calling config files

(defun goto-init.el ()
  "Open init.el file"
  (interactive)
  (find-file "~/.emacs.d/init.el"))
(defun goto-custom.el ()
  "Open custom.el file"
  (interactive)
  (find-file "~/.emacs.d/custom.el"))
(defun goto-config.org ()
  "Open config.org file"
  (interactive)
  (find-file "~/.emacs.d/config.org"))
(defun cpm/compile-dotemacs ()
  "Byte compile all files in the .emacs.d base directory"
  (interactive)
  (byte-recompile-directory cpm-emacs-dir 0 t))
(defun load-config ()
  "Load config "
  (interactive)
  (cpm/tangle-emacs-config)
  (load-file "~/.emacs.d/init.el"))
(defun goto-dotfiles.org ()
  "Open dotfiles.org file"
  (interactive)
  (find-file "~/dotfiles/dotfiles.org"))
(defun goto-emacs-dir ()
  "Open dotfiles.org file"
  (interactive)
  (require 'ranger)
   (find-file "~/.emacs.d"))
(defun goto-org-files ()
  "Open directory with org files"
  (interactive)
  (require 'ranger)
  (find-file org-directory))

Copy formatted org-mode text to rtf

Via the always resourceful John Kitchin.

  (defun formatted-copy ()
  "Export region to HTML, and copy it to the clipboard."
  (interactive)
  (save-window-excursion
    (let* ((buf (org-export-to-buffer 'html "*Formatted Copy*" nil nil t t))
           (html (with-current-buffer buf (buffer-string))))
      (with-current-buffer buf
        (shell-command-on-region
         (point-min)
         (point-max)
         "textutil -stdin -format html -convert rtf -stdout | pbcopy")) 
      (kill-buffer buf))))

(global-set-key (kbd "H-w") 'formatted-copy)

Crux

A collection of ridiculously useful extensions. Indeed.

(use-package crux :defer 10)

Cycle Through Useful Buffers

From a useful post by Xah.

(defun cpm/user-buffer-q ()
  "Return t if current buffer is a user buffer, else nil.
Typically, if buffer name starts with *, it's not considered a user buffer.
This function is used by buffer switching command and close buffer command, so that next buffer shown is a user buffer.
You can override this function to get your idea of “user buffer”.
version 2016-06-18"
  (interactive)
  (if (string-equal "*" (substring (buffer-name) 0 1))
      nil
    (if (string-equal major-mode "dired-mode")
        nil
      t
      )))

(defun cpm/next-user-buffer ()
  "Switch to the next user buffer.
“user buffer” is determined by `cpm/user-buffer-q'.
URL `http://ergoemacs.org/emacs/elisp_next_prev_user_buffer.html'
Version 2016-06-19"
  (interactive)
  (next-buffer)
  (let ((i 0))
    (while (< i 20)
      (if (not (cpm/user-buffer-q))
          (progn (next-buffer)
                 (setq i (1+ i)))
        (progn (setq i 100))))))

(defun cpm/previous-user-buffer ()
  "Switch to the previous user buffer.
“user buffer” is determined by `cpm/user-buffer-q'.
URL `http://ergoemacs.org/emacs/elisp_next_prev_user_buffer.html'
Version 2016-06-19"
  (interactive)
  (previous-buffer)
  (let ((i 0))
    (while (< i 20)
      (if (not (cpm/user-buffer-q))
          (progn (previous-buffer)
                 (setq i (1+ i)))
        (progn (setq i 100))))))

Delete Current File

;; from magnars
(defun cpm/delete-current-buffer-file ()
  "Removes file connected to current buffer and kills buffer."
  (interactive)
  (let ((filename (buffer-file-name))
        (buffer (current-buffer))
        (name (buffer-name)))
    (if (not (and filename (file-exists-p filename)))
        (ido-kill-buffer)
      (when (yes-or-no-p "Are you sure you want to delete this file? ")
        (delete-file filename t)
        (kill-buffer buffer)
        (message "File '%s' successfully removed" filename)))))

Delete Dotemacs Byte Files

(defun cpm/delete-byte-compiled-files ()
  (interactive)
  (shell-command-to-string "trash ~/.emacs.d/*.elc"))

Duplicate file

Duplicate a file in dired or deer

(defun cpm/duplicate-file ()
  (interactive)
  (dired-do-copy-regexp "\\(.*\\)\\.\\(.*\\)" "\\1 (copy).\\2"))

Escape all the Things!!

From a great vim migration guide by Juanjo Álvarez (original code from davvil).

;; esc quits
(defun minibuffer-keyboard-quit ()
  "Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
  (interactive)
  (if (and delete-selection-mode transient-mark-mode mark-active)
      (setq deactivate-mark  t)
    (when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
    (abort-recursive-edit)))
(with-eval-after-load 'evil
(define-key evil-normal-state-map [escape] 'keyboard-quit)
(define-key evil-visual-state-map [escape] 'keyboard-quit)
(define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
(global-set-key [escape] 'evil-exit-emacs-state))

Eval emacs buffer until error

(defun cpm/eval-buffer-until-error ()
"Evaluate emacs buffer until error occured."
(interactive)
(goto-char (point-min))
(while t (eval (read (current-buffer)))))

Fill/Unfill Paragraphs

Artur Malabarba has a useful discussion of how to fill/unfill paragraphs with the same command.

(defun cpm/fill-or-unfill ()
  "Like `fill-paragraph', but unfill if used twice."
  (interactive)
  (let ((fill-column
         (if (eq last-command 'cpm/fill-or-unfill)
             (progn (setq this-command nil)
                    (point-max))
           fill-column)))
    (call-interactively #'fill-paragraph)))

(global-set-key [remap fill-paragraph]
                #'cpm/fill-or-unfill)

Goto journal

(defun cpm/goto-journal ()
  (interactive)
  (find-file "/Users/Roambot/Dropbox/org-files/journal.org"))

Insert Seconds (Epoch) Time Stamp

(defun cpm/insert-seconds-epoch ()
  (interactive)
  (insert (format-time-string "%s"))) ; the integer number of seconds since the epoch
(global-set-key (kbd "C-c t") 'cpm/insert-seconds-epoch)

Jump in buffer

I got the inspiration for this from the spacemacs config. Useful for navigating in tagged buffers.

(defun cpm/jump-in-buffer ()
  (interactive)
    (cond
     ((eq major-mode 'org-mode)
      (call-interactively 'counsel-org-goto))
     (t
      (call-interactively 'helm-semantic-or-imenu))))
      
;; resume last jump
(defun cpm/resume-last-jump ()
  (interactive)
    (cond
     ((eq major-mode 'org-mode)
      (call-interactively 'ivy-resume))
     (t
      (call-interactively 'helm-resume))))

Jump to sexp

(defun cpm/forward-or-backward-sexp (&optional arg)
  "Go to the matching parenthesis character if one is adjacent to point."
  (interactive "^p")
  (cond ((looking-at "\\s(") (forward-sexp arg))
        ((looking-back "\\s)" 1) (backward-sexp arg))
        ;; Now, try to succeed from inside of a bracket
        ((looking-at "\\s)") (forward-char) (backward-sexp arg))
        ((looking-back "\\s(" 1) (backward-char) (forward-sexp arg))))

Make move

(defun cpm/make-move ()
  "move files to project web directory"
   (interactive)
   (evil-ex "!make move"))

Make parent directory

Create a directory – or a hierarchy of them – while finding a file in a nonexistent directory. From mbork.

(defun make-parent-directory ()
  "Make sure the directory of `buffer-file-name' exists."
  (make-directory (file-name-directory buffer-file-name) t))

(add-hook 'find-file-not-found-functions #'make-parent-directory)

Move File

(defun cpm/move-file ()
  "Write this file to a new location, and delete the old one."
  (interactive)
  (let ((old-location (buffer-file-name)))
    (call-interactively #'write-file)
    (when old-location
      (delete-file old-location))))

Narrow or Widen

Narrow a region, or if narrowed, widen. Courtesy of the ever resourceful Artur Malabarba.

(defun cpm/narrow-or-widen-dwim (p)
  "Widen if buffer is narrowed, narrow-dwim otherwise.
Dwim means: region, org-src-block, org-subtree, or
defun, whichever applies first. Narrowing to
org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer
is already narrowed."
  (interactive "P")
  (declare (interactive-only))
  (cond ((and (buffer-narrowed-p) (not p)) (widen))
        ((region-active-p)
         (narrow-to-region (region-beginning)
                           (region-end)))
        ((derived-mode-p 'org-mode)
         ;; `org-edit-src-code' is not a real narrowing
         ;; command. Remove this first conditional if
         ;; you don't want it.
         (cond ((ignore-errors (org-edit-src-code) t)
                (delete-other-windows))
               ((ignore-errors (org-narrow-to-block) t))
               (t (org-narrow-to-subtree))))
        ((derived-mode-p 'latex-mode)
         (LaTeX-narrow-to-environment))
        (t (narrow-to-defun))))

New Git Project

Courtesy of a helpful reddit post

(defun cpm/git-new-project ()
  "Initializes a new git repo and adds it to projectile's known projects."
  (interactive)
  (let ((project-dir (expand-file-name
                      (read-directory-name "New project root:"))))
    (magit-init project-dir)
    (projectile-add-known-project project-dir)
    (setq default-directory project-dir)))

Open projects directory

(defun cpm/goto-projects ()
    "Open projects dir"
    (interactive)
    (require 'ranger)
    (find-file "~/Dropbox/Work/projects"))

Org Tree to File

Send an org tree to its own file. Inspiration from this superuser answer.

(defun cpm/subtree-to-new-file ()
  (interactive)
  "Move an org subtree to a new file"
  (org-copy-subtree nil t)
  (find-file-other-window  
    (read-file-name "Move subtree to file:" "$HOME"))
(org-paste-subtree))

Org wrap in block template

A helpful function I found here for wrapping text in a block template.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; function to wrap blocks of text in org templates                       ;;
;; e.g. latex or src etc                                                  ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun org-block-wrap ()
  "Make a template at point."
  (interactive)
  (if (org-at-table-p)
      (call-interactively 'org-table-rotate-recalc-marks)
    (let* ((choices '(
                      ("a" . "ASCII")
                      ("c" . "COMMENT")
                      ("C" . "CENTER")
                      ("e" . "EXAMPLE")
                      ("E" . "SRC emacs-lisp")
                      ("h" . "HTML")
                      ("l" . "LaTeX")
                      ("n" . "NOTES")
                      ("q" . "QUOTE")
                      ("s" . "SRC")
                      ("v" . "VERSE")
                      ))
           (key
            (key-description
             (vector
              (read-key
               (concat (propertize "Template type: " 'face 'minibuffer-prompt)
                       (mapconcat (lambda (choice)
                                    (concat (propertize (car choice) 'face 'font-lock-type-face)
                                            ": "
                                            (cdr choice)))
                                  choices
                                  ", ")))))))
      (let ((result (assoc key choices)))
        (when result
          (let ((choice (cdr result)))
            (cond
             ((region-active-p)
              (let ((start (region-beginning))
                    (end (region-end)))
                (goto-char end)
                (insert "#+END_" choice "\n")
                (goto-char start)
                (insert "#+BEGIN_" choice "\n")))
             (t
              (insert "#+BEGIN_" choice "\n")
              (save-excursion (insert "#+END_" choice))))))))))

Pandoc conversion from clipboard

(defun cpm/org-to-markdown ()
  "convert clipboard contents from org to markdown and paste"
  (interactive)
  (kill-new (shell-command-to-string "osascript -e 'the clipboard as unicode text' | pandoc --atx-headers -f org -t markdown"))
  (yank))

(defun cpm/markdown-to-org ()
  "convert clipboard contents from markdown to org and paste"
  (interactive)
  (kill-new (shell-command-to-string "osascript -e 'the clipboard as unicode text' | pandoc -f markdown -t org"))
  (yank))

(defun cpm/tex-to-org ()
  "convert clipboard contents from markdown to org and paste"
  (interactive)
  (kill-new (shell-command-to-string "osascript -e 'the clipboard as unicode text' | pandoc -f latex -t org --atx-headers"))
  (yank))

(defun cpm/tex-to-markdown ()
  "convert clipboard contents from markdown to org and paste"
  (interactive)
  (kill-new (shell-command-to-string "osascript -e 'the clipboard as unicode text' | pandoc -f latex -t markdown --atx-headers"))
  (yank))
  
(defun cpm/markdown-to-tex ()
  "convert clipboard contents from markdown to org and paste"
  (interactive)
  (kill-new (shell-command-to-string "osascript -e 'the clipboard as unicode text' | pandoc -f markdown -t latex"))
  (yank))

(defun cpm/cite-to-org ()
  "convert clipboard contents from markdown to org with citations and paste"
  (interactive)
  (kill-new (shell-command-to-string "osascript -e 'the clipboard as unicode text' | pandoc --bibliography=/Users/Roambot/Dropbox/Work/Master.bib -s -t markdown-native_divs-raw_html-citations | pandoc -f markdown -t org"))
  (yank))

(defun cpm/cite-to-markdown ()
  "convert clipboard contents to markdown with citations and paste"
  (interactive)
  (kill-new (shell-command-to-string "osascript -e 'the clipboard as unicode text' | pandoc --bibliography=/Users/Roambot/Dropbox/Work/Master.bib -s -t markdown-native_divs-raw_html-citations --atx-headers"))
  (yank))

Projectile Find File Other Window

Find a file in a project and open in a vertical split

(defun cpm/helm-projectile-find-file-other-window ()
 "Find a file in a project and open in a vertical split"
 (interactive)
 (cpm/split-window-right-and-focus)
 (helm-projectile-find-file))

Resume last search

(defun cpm/last-search-buffer ()
      "open last helm-ag or hgrep buffer."
      (interactive)
      (cond ((get-buffer "*helm ag results*")
             (switch-to-buffer-other-window "*helm ag results*"))
            ((get-buffer "*helm-ag*")
             (helm-resume "*helm-ag*"))
            ((get-buffer "*hgrep*")
             (switch-to-buffer-other-window "*hgrep*"))
            ((get-buffer "*helm occur*")
             (helm-resume "*helm occur*"))
            (t
             (message "No previous search buffer found"))))

Reveal in Finder

(defun cpm/browse-file-directory ()
  "Open the current file's directory however the OS would."
  (interactive)
  (if default-directory
      (browse-url-of-file (expand-file-name default-directory))
    (error "No `default-directory' to open")))

Reveal to PDF

(defun cpm/reveal-to-pdf ()
  "print reveal.js slides to pdf"
  (interactive)
  (async-shell-command "phantomjs ~/bin/print-pdf.js 'file:///Users/roambot/Dropbox/Work/projects/phil105/phil105-classplan.html?print-pdf'")
  (delete-windows-on "*Async Shell Command*" t))

Rotate windows

;; from magnars modified by ffevotte for dedicated windows support
(defun cpm/rotate-windows (count)
  "Rotate your windows.
Dedicated windows are left untouched. Giving a negative prefix
argument takes the kindows rotate backwards."
  (interactive "p")
  (let* ((non-dedicated-windows (remove-if 'window-dedicated-p (window-list)))
         (num-windows (length non-dedicated-windows))
         (i 0)
         (step (+ num-windows count)))
    (cond ((not (> num-windows 1))
           (message "You can't rotate a single window!"))
          (t
           (dotimes (counter (- num-windows 1))
             (let* ((next-i (% (+ step i) num-windows))

                    (w1 (elt non-dedicated-windows i))
                    (w2 (elt non-dedicated-windows next-i))

                    (b1 (window-buffer w1))
                    (b2 (window-buffer w2))

                    (s1 (window-start w1))
                    (s2 (window-start w2)))
               (set-window-buffer w1 b2)
               (set-window-buffer w2 b1)
               (set-window-start w1 s2)
               (set-window-start w2 s1)
               (setq i next-i)))))))

(defun cpm/rotate-windows-backward (count)
  "Rotate your windows backward."
  (interactive "p")
  (rotate-windows (* -1 count)))

Search directories with ag

(defun cpm/helm-files-do-ag (&optional dir)
  "Search in files with `ag' using a default input."
    (interactive)
    (helm-do-ag dir))
        
(defun cpm/helm-files-search-current-directory ()
  "search in files with `ag' in current buffer's directory"
    (interactive)
    (helm-do-ag (file-name-directory buffer-file-name)))

Show Filename of Buffer

;; http://camdez.com/blog/2013/11/14/emacs-show-buffer-file-name/
(defun cpm/show-and-copy-buffer-filename ()
  "Show the full path to the current file in the minibuffer."
  (interactive)
  (let ((file-name (buffer-file-name)))
    (if file-name
        (progn
          (message file-name)
          (kill-new file-name))
      (error "Buffer not visiting a file"))))

Show Next Spelling Error

Go to the next spelling error using flyspell and ispell. From the Wiki.

(defun cpm/flyspell-ispell-goto-next-error ()
  "Custom function to spell check next highlighted word"
  (interactive)
  (flyspell-goto-next-error)
  (ispell-word)
  )

Smart Yanking

Courtesy of Marcin Borkowski

(defun has-space-at-boundary-p (string)
  "Check whether STRING has any whitespace on the boundary.
Return 'left, 'right, 'both or nil."
  (let ((result nil))
    (when (string-match-p "^[[:space:]]+" string)
      (setq result 'left))
    (when (string-match-p "[[:space:]]+$" string)
      (if (eq result 'left)
	  (setq result 'both)
	(setq result 'right)))
    result))

(defun is-there-space-around-point-p ()
  "Check whether there is whitespace around point.
Return 'left, 'right, 'both or nil."
  (let ((result nil))
    (when (< (save-excursion
               (skip-chars-backward "[:space:]"))
             0)
      (setq result 'left))
    (when (> (save-excursion
               (skip-chars-forward "[:space:]"))
             0)
      (if (eq result 'left)
	  (setq result 'both)
	(setq result 'right)))
    result))

(defun set-point-before-yanking (string)
  "Put point in the appropriate place before yanking STRING."
  (let ((space-in-yanked-string (has-space-at-boundary-p string))
	(space-at-point (is-there-space-around-point-p)))
    (cond ((and (eq space-in-yanked-string 'left)
		(eq space-at-point 'left))
	   (skip-chars-backward "[:space:]"))
	  ((and (eq space-in-yanked-string 'right)
		(eq space-at-point 'right))
	   (skip-chars-forward "[:space:]")))))

(defun set-point-before-yanking-if-in-text-mode (string)
  "Invoke `set-point-before-yanking' in text modes."
  (when (derived-mode-p 'text-mode)
    (set-point-before-yanking string)))

(advice-add
 'insert-for-yank
 :before
 #'set-point-before-yanking-if-in-text-mode)

Sticky Buffer/Window

Stick/Lock buffer to window, courtesy of ShingoFukuyama.

;; http://lists.gnu.org/archive/html/help-gnu-emacs/2007-05/msg00975.html

(defvar sticky-buffer-previous-header-line-format)
(define-minor-mode sticky-buffer-mode
  "Make the current window always display this buffer."
  nil " sticky" nil
  (if sticky-buffer-mode
      (progn
        (set (make-local-variable 'sticky-buffer-previous-header-line-format)
             header-line-format)
        (set-window-dedicated-p (selected-window) sticky-buffer-mode))
    (set-window-dedicated-p (selected-window) sticky-buffer-mode)
    (setq header-line-format sticky-buffer-previous-header-line-format)))

Swap windows

Swap buffers in windows and leave the cursor in the original window. Courtesy of Mike Zamansky’s video.

(defun cpm/window-exchange ()
"Swap buffer windows and leave focus in original window"
(interactive)
(ace-swap-window)
(aw-flip-window)
)

Switch to previous buffer

(defun switch-to-previous-buffer ()
  (interactive)
  (switch-to-buffer (other-buffer (current-buffer) 1)))

Tangle file on save

(defun cpm/tangle-emacs-config ()
  "If the current file is in '~/.emacs.d/', the code blocks are tangled"
  (when (equal (file-name-directory (directory-file-name buffer-file-name))
               (concat (getenv "HOME") "/.emacs.d/"))
    (org-babel-tangle)
    (message "%s tangled" buffer-file-name)))

;; (add-hook 'after-save-hook #'cpm/tangle-emacs-config)

Toggle Window Split

Move from a horizontal to a vertical split and vice versa

(defun cpm/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))))))

Search TODO Markers

Make an equivalent of vim’s quickfix buffer using helm-ag and highlight-todo

(defun cpm/search-file-todo-markers ()
    "Search for any TODO markers as specified in hl-todo-keyword-faces.

Note that this uses the word boundary \\b to avoid matching these
within other words, but this means that non-word keywords such as
???, which is in the list by default, will not be matched."
    (interactive)
    (require 'projectile)

    (let* ((grouped (funcall #'regexp-opt (--map (car it) hl-todo-keyword-faces)))
           (unescaped (s-replace-all '(("\\(" . "(") ("\\)" . ")") ("\\|" . "|"))
                                     grouped))
           (bounded (concat "\\b" unescaped "\\b"))
           (helm-follow-mode-persistent t))
      (helm-do-ag-this-file bounded)))

(defun cpm/search-todo-markers ()
    "Search for any TODO markers as specified in hl-todo-keyword-faces.

Note that this uses the word boundary \\b to avoid matching these
within other words, but this means that non-word keywords such as
???, which is in the list by default, will not be matched."
    (interactive)
    (require 'projectile)

    (let* ((grouped (funcall #'regexp-opt (--map (car it) hl-todo-keyword-faces)))
           (unescaped (s-replace-all '(("\\(" . "(") ("\\)" . ")") ("\\|" . "|"))
                                     grouped))
           (bounded (concat "\\b" unescaped "\\b"))
           (helm-follow-mode-persistent t))
      (helm-do-ag (projectile-project-root) nil bounded)))

DOOM! Macros

A set of fantastic macros written by hlissner. There won’t be much documentation around these because the comments for each macro does a great job explaining their function. For more information you can also look at the wiki and the entry on macros in particular.

after!

(defmacro after! (feature &rest forms)
  "A smart wrapper around `with-eval-after-load'. Supresses warnings during
compilation."
  (declare (indent defun) (debug t))
  `(,(if (or (not (bound-and-true-p byte-compile-current-file))
             (if (symbolp feature)
                 (require feature nil :no-error)
               (load feature :no-message :no-error)))
         #'progn
       #'with-no-warnings)
    (with-eval-after-load ',feature ,@forms)))

map!

(eval-and-compile
  (defun cmacs-enlist (exp)
    "Return EXP wrapped in a list, or as-is if already a list."
    (if (listp exp) exp (list exp)))

  (defun doom-unquote (exp)
    "Return EXP unquoted."
    (while (memq (car-safe exp) '(quote function))
      (setq exp (cadr exp)))
    exp)

  (defvar cmacs-evil-state-alist
    '((?n . normal)
      (?v . visual)
      (?i . insert)
      (?e . emacs)
      (?o . operator)
      (?m . motion)
      (?r . replace))
    "A list of cons cells that map a letter to a evil state symbol.")

  ;; Register keywords for proper indentation (see `map!')
  (put ':after        'lisp-indent-function 'defun)
  (put ':desc         'lisp-indent-function 'defun)
  (put ':leader       'lisp-indent-function 'defun)
  (put ':local        'lisp-indent-function 'defun)
  (put ':localleader  'lisp-indent-function 'defun)
  (put ':map          'lisp-indent-function 'defun)
  (put ':map*         'lisp-indent-function 'defun)
  (put ':mode         'lisp-indent-function 'defun)
  (put ':prefix       'lisp-indent-function 'defun)
  (put ':textobj      'lisp-indent-function 'defun)
  (put ':unless       'lisp-indent-function 'defun)
  (put ':when         'lisp-indent-function 'defun)

;; specials
  (defvar cmacs--keymaps nil)
  (defvar cmacs--prefix  nil)
  (defvar cmacs--defer   nil)
  (defvar cmacs--local   nil)

(defun cmacs--keybind-register (key desc &optional modes)
  "Register a description for KEY with `which-key' in MODES.

  KEYS should be a string in kbd format.
  DESC should be a string describing what KEY does.
  MODES should be a list of major mode symbols."
  (if modes
      (dolist (mode modes)
        (which-key-add-major-mode-key-based-replacements mode key desc))
    (which-key-add-key-based-replacements key desc)))

(defun cmacs--keyword-to-states (keyword)
  "Convert a KEYWORD into a list of evil state symbols.

For example, :nvi will map to (list 'normal 'visual 'insert). See
`cmacs-evil-state-alist' to customize this."
  (cl-loop for l across (substring (symbol-name keyword) 1)
           if (cdr (assq l cmacs-evil-state-alist))
             collect it
           else
             do (error "not a valid state: %s" l)))

(defmacro map! (&rest rest)
  "A nightmare of a key-binding macro that will use `evil-define-key*',
`define-key', `local-set-key' and `global-set-key' depending on context and
plist key flags (and whether evil is loaded or not). It was designed to make
binding multiple keys more concise, like in vim.

If evil isn't loaded, it will ignore evil-specific bindings.

States
    :n  normal
    :v  visual
    :i  insert
    :e  emacs
    :o  operator
    :m  motion
    :r  replace

    These can be combined (order doesn't matter), e.g. :nvi will apply to
    normal, visual and insert mode. The state resets after the following
    key=>def pair.

    If states are omitted the keybind will be global.

    This can be customized with `cmacs-evil-state-alist'.

    :textobj is a special state that takes a key and two commands, one for the
    inner binding, another for the outer.

Flags
    (:mode [MODE(s)] [...])    inner keybinds are applied to major MODE(s)
    (:map [KEYMAP(s)] [...])   inner keybinds are applied to KEYMAP(S)
    (:map* [KEYMAP(s)] [...])  same as :map, but deferred
    (:prefix [PREFIX] [...])   assign prefix to all inner keybindings
    (:after [FEATURE] [...])   apply keybinds when [FEATURE] loads
    (:local [...])             make bindings buffer local; incompatible with keymaps!

Conditional keybinds
    (:when [CONDITION] [...])
    (:unless [CONDITION] [...])

Example
    (map! :map magit-mode-map
          :m \"C-r\" 'do-something           ; assign C-r in motion state
          :nv \"q\" 'magit-mode-quit-window  ; assign to 'q' in normal and visual states
          \"C-x C-r\" 'a-global-keybind

          (:when IS-MAC
           :n \"M-s\" 'some-fn
           :i \"M-o\" (lambda (interactive) (message \"Hi\"))))"
  (let ((cmacs--keymaps cmacs--keymaps)
        (cmacs--prefix  cmacs--prefix)
        (cmacs--defer   cmacs--defer)
        (cmacs--local   cmacs--local)
        key def states forms desc modes)
    (while rest
      (setq key (pop rest))
      (cond
	;; it's a sub expr
	((listp key)
        (push (macroexpand `(map! ,@key)) forms))

	;; it's a flag
	((keywordp key)
        (cond ((eq key :leader)
		(push 'cmacs-leader-key rest)
		(setq key :prefix
                     desc "<leader>"))
              ((eq key :localleader)
		(push 'cmacs-localleader-key rest)
		(setq key :prefix
                     desc "<localleader>")))
        (pcase key
          (:when    (push `(if ,(pop rest)       ,(macroexpand `(map! ,@rest))) forms) (setq rest '()))
          (:unless  (push `(if (not ,(pop rest)) ,(macroexpand `(map! ,@rest))) forms) (setq rest '()))
          (:after   (push `(after! ,(pop rest)   ,(macroexpand `(map! ,@rest))) forms) (setq rest '()))
          (:desc    (setq desc (pop rest)))
          (:map*    (setq cmacs--defer t) (push :map rest))
          (:map
            (setq cmacs--keymaps (cmacs-enlist (pop rest))))
          (:mode
            (setq modes (cmacs-enlist (pop rest)))
            (unless cmacs--keymaps
              (setq cmacs--keymaps
                    (cl-loop for m in modes
                             collect (intern (format "%s-map" (symbol-name m)))))))
          (:textobj
            (let* ((key (pop rest))
                   (inner (pop rest))
                   (outer (pop rest)))
              (push (macroexpand `(map! (:map evil-inner-text-objects-map ,key ,inner)
                                        (:map evil-outer-text-objects-map ,key ,outer)))
                    forms)))
          (:prefix
            (let ((def (pop rest)))
              (setq cmacs--prefix `(vconcat ,cmacs--prefix (kbd ,def)))
              (when desc
                (push `(cmacs--keybind-register ,(key-description (eval cmacs--prefix))
                                                ,desc ',modes)
                      forms)
                (setq desc nil))))
          (:local
           (setq cmacs--local t))
          (_ ; might be a state cmacs--prefix
           (setq states (cmacs--keyword-to-states key)))))

	;; It's a key-def pair
	((or (stringp key)
            (characterp key)
            (vectorp key)
            (symbolp key))
        (unwind-protect
            (catch 'skip
              (when (symbolp key)
                (setq key `(kbd ,key)))
              (when (stringp key)
                (setq key (kbd key)))
              (when cmacs--prefix
                (setq key (append cmacs--prefix (list key))))
              (unless (> (length rest) 0)
                (user-error "map! has no definition for %s key" key))
              (setq def (pop rest))
              (when desc
                (push `(cmacs--keybind-register ,(key-description (eval key))
                                              ,desc ',modes)
                      forms))
              (cond ((and cmacs--local cmacs--keymaps)
                     (push `(lwarn 'cmacs-map :warning
                                   "Can't local bind '%s' key to a keymap; skipped"
                                   ,key)
                           forms)
                     (throw 'skip 'local))
                    ((and cmacs--keymaps states)
                     (dolist (keymap cmacs--keymaps)
			(push `(,(if cmacs--defer 'evil-define-key 'evil-define-key*)
				',states ,keymap ,key ,def)
                             forms)))
                    (states
                     (dolist (state states)
			(push `(define-key
                                ,(intern (format "evil-%s-state-%smap" state (if cmacs--local "local-" "")))
                                ,key ,def)
                             forms)))
                    (cmacs--keymaps
                     (dolist (keymap cmacs--keymaps)
			(push `(define-key ,keymap ,key ,def) forms)))
                    (t
                     (push `(,(if cmacs--local 'local-set-key 'global-set-key) ,key ,def)
                           forms))))
          (setq states '()
                cmacs--local nil
                desc nil)))

	(t (user-error "Invalid key %s" key))))
    `(progn ,@(nreverse forms)))))

add-hook!

A macro that makes adding hooks easy

(eval-and-compile
  (defun cmacs--resolve-hook-forms (hooks)
    (cl-loop with quoted-p = (eq (car-safe hooks) 'quote)
             for hook in (cmacs-enlist (doom-unquote hooks))
             if (eq (car-safe hook) 'quote)
              collect (cadr hook)
             else if quoted-p
              collect hook
             else collect (intern (format "%s-hook" (symbol-name hook)))))

  (defvar cmacs--transient-counter 0)
  (defmacro add-transient-hook! (hook &rest forms)
    "Attaches transient forms to a HOOK.

  HOOK can be a quoted hook or a sharp-quoted function (which will be advised).

  These forms will be evaluated once when that function/hook is first invoked,
  then it detaches itself."
    (declare (indent 1))
    (let ((append (eq (car forms) :after))
          (fn (intern (format "cmacs-transient-hook-%s" (cl-incf cmacs--transient-counter)))))
      `(when ,hook
         (fset ',fn
		(lambda (&rest _)
                 ,@forms
                 (cond ((functionp ,hook) (advice-remove ,hook #',fn))
			((symbolp ,hook)   (remove-hook ,hook #',fn)))
                 (unintern ',fn nil)))
         (cond ((functionp ,hook)
                (advice-add ,hook ,(if append :after :before) #',fn))
		((symbolp ,hook)
                (add-hook ,hook #',fn ,append)))))))

(defmacro add-hook! (&rest args)
  "A convenience macro for `add-hook'. Takes, in order:

  1. Optional properties :local and/or :append, which will make the hook
     buffer-local or append to the list of hooks (respectively),
  2. The hooks: either an unquoted major mode, an unquoted list of major-modes,
     a quoted hook variable or a quoted list of hook variables. If unquoted, the
     hooks will be resolved by appending -hook to each symbol.
  3. A function, list of functions, or body forms to be wrapped in a lambda.

Examples:
    (add-hook! 'some-mode-hook 'enable-something)
    (add-hook! some-mode '(enable-something and-another))
    (add-hook! '(one-mode-hook second-mode-hook) 'enable-something)
    (add-hook! (one-mode second-mode) 'enable-something)
    (add-hook! :append (one-mode second-mode) 'enable-something)
    (add-hook! :local (one-mode second-mode) 'enable-something)
    (add-hook! (one-mode second-mode) (setq v 5) (setq a 2))
    (add-hook! :append :local (one-mode second-mode) (setq v 5) (setq a 2))

Body forms can access the hook's arguments through the let-bound variable
`args'."
  (declare (indent defun) (debug t))
  (let ((hook-fn 'add-hook)
        append-p local-p)
    (while (keywordp (car args))
      (pcase (pop args)
        (:append (setq append-p t))
        (:local  (setq local-p t))
        (:remove (setq hook-fn 'remove-hook))))
    (let ((hooks (cmacs--resolve-hook-forms (pop args)))
          (funcs
           (let ((val (car args)))
             (if (memq (car-safe val) '(quote function))
                 (if (cdr-safe (cadr val))
                     (cadr val)
                   (list (cadr val)))
		(list args))))
          forms)
      (dolist (fn funcs)
        (setq fn (if (symbolp fn)
                     `(function ,fn)
                   `(lambda (&rest _) ,@args)))
        (dolist (hook hooks)
          (push (cond ((eq hook-fn 'remove-hook)
			`(remove-hook ',hook ,fn ,local-p))
                      (t
			`(add-hook ',hook ,fn ,append-p ,local-p)))
                forms)))
      `(progn ,@(nreverse forms)))))

(defmacro remove-hook! (&rest args)
  "Convenience macro for `remove-hook'. Takes the same arguments as
`add-hook!'."
  `(add-hook! :remove ,@args))

quiet!

A simple macro that prevents code from making any noise

(defmacro quiet! (&rest forms)
  "Run FORMS without making any noise."
  `(if nil
	(progn ,@forms)
     (fset 'doom--old-write-region-fn (symbol-function 'write-region))
     (cl-letf ((standard-output (lambda (&rest _)))
		((symbol-function 'load-file) (lambda (file) (load file nil t)))
		((symbol-function 'message) (lambda (&rest _)))
		((symbol-function 'write-region)
                (lambda (start end filename &optional append visit lockname mustbenew)
                  (unless visit (setq visit 'no-message))
                  (doom--old-write-region-fn
                   start end filename append visit lockname mustbenew)))
		(inhibit-message t)
		(save-silently t))
	,@forms)))

def-memoized!

Creates a memoized function

(defvar doom-memoized-table (make-hash-table :test 'equal :size 10)
  "A lookup table containing memoized functions. The keys are argument lists,
and the value is the function's return value.")

(defun doom-memoize (name)
  "Memoizes an existing function. NAME is a symbol."
  (let ((func (symbol-function name)))
    (put name 'function-documentation
         (concat (documentation func) " (memoized)"))
    (fset name
          `(lambda (&rest args)
             (let ((key (cons ',name args)))
		(or (gethash key doom-memoized-table)
                   (puthash key (apply ',func args)
                            doom-memoized-table)))))))

(defmacro def-memoized! (name arglist &rest body)
  "Create a memoize'd function. NAME, ARGLIST, DOCSTRING and BODY
have the same meaning as in `defun'."
  (declare (indent defun) (doc-string 3))
  `(,(if (bound-and-true-p byte-compile-current-file)
         'with-no-warnings
	'progn)
     (defun ,name ,arglist ,@body)
     (doom-memoize ',name)))

λ!

(defmacro λ! (&rest body)
  "A shortcut for inline interactive lambdas."
  (declare (doc-string 1))
  `(lambda () (interactive) ,@body))

Other Macros

(defmacro find-file-in! (path &optional project-p)
  "Returns a interactive function for searching files"
  `(lambda () (interactive)
     (let ((default-directory ,path))
       (call-interactively
        ',(if project-p
              (command-remapping 'projectile-find-file)
            (command-remapping 'find-file))))))

Emacs Settings

System Defaults

Let’s use sane defaults. Sources for this section include Magnars Sveen and Sacha Chua.

Apropos Everything

apropos commands perform more extensive searches than default

(setq apropos-do-all t)

Clipboard

; Merge system's and Emacs' clipboard
(setq select-enable-clipboard t)
;; Save whatever’s in the current (system) clipboard before
;; replacing it with the Emacs’ text.
(setq save-interprogram-paste-before-kill t)
;; Copy/Paste functions 
;; https://github.com/dakrone/eos/blob/master/eos-core.org#mac-osx
(defun copy-from-osx ()
    "Handle copy/paste intelligently on osx."
    (let ((pbpaste (purecopy "/usr/bin/pbpaste")))
      (if (and (eq system-type 'darwin)
               (file-exists-p pbpaste))
          (let ((tramp-mode nil)
                (default-directory "~"))
            (shell-command-to-string pbpaste)))))

  (defun paste-to-osx (text &optional push)
    (let ((process-connection-type nil))
      (let ((proc (start-process "pbcopy" "*Messages*" "/usr/bin/pbcopy")))
        (process-send-string proc text)
        (process-send-eof proc))))
  (setq interprogram-cut-function 'paste-to-osx
        interprogram-paste-function 'copy-from-osx)

Cursor Movement

Per this post on stack overflow

(setq auto-window-vscroll nil)

Disable Welcome Screen

; Disable start-up screen
(setq-default inhibit-startup-screen t)                         
(setq inhibit-splash-screen t)
(setq inhibit-startup-message t)
(setq initial-scratch-message "")
;; And bury the scratch buffer, don't kill it
(defadvice kill-buffer (around kill-buffer-around-advice activate)
  (let ((buffer-to-kill (ad-get-arg 0)))
    (if (equal buffer-to-kill "*scratch*")
        (bury-buffer)
      ad-do-it)))

Environment Path

Make sure emacs correctly sets up your PATH.

(defvar cpm-local-bin (concat (getenv "HOME") "/bin") "Local execs.")
(defvar usr-local-bin "/usr/local/bin")
(defvar usr-local-sbin "/usr/local/sbin")
(setenv "PATH" (concat usr-local-bin ":" usr-local-sbin ":" (getenv "PATH") ":" cpm-local-bin))
(setq exec-path (append exec-path (list cpm-local-bin usr-local-sbin usr-local-bin)))

File Endings

Make all files POSIX compliant for newlines

;; Make sure your text files end in a newline
(setq require-final-newline t)

Give buffers unique names

(setq uniquify-buffer-name-style 'forward)

Help Buffers

;; Keep focus while navigating help buffers
(setq help-window-select 't)

Large Files

Warn only for files over 100MB

(setq large-file-warning-threshold 100000000)

Paragraphs

From the Emacs Wiki on fill paragraph.
;; The original value is "\f\\|[      ]*$", so we add the bullets (-), (+), and (*).
    ;; There is no need for "^" as the regexp is matched at the beginning of line.
    (setq paragraph-start "\f\\|[ \t]*$\\|[ \t]*[-+*] ")

Pretty symbols

(setq prettify-symbols-unprettify-at-point t)
(global-prettify-symbols-mode +1)

Startup Echo Message

  ;; Change the echo message
(defun display-startup-echo-area-message ()
  (message ""))

Scratch startup mode

Useful to get a faster Emacs load time because it avoids autoloads of elisp modes or other minor modes. I start it in org-mode though because that seems to ease loading when launching org projects.

(setq initial-major-mode 'org-mode)

Text Settings

General Text settings and hooks

Sentence endings

;; Single space between sentences is more widespread than double
(setq-default sentence-end-double-space nil)

Subwords and CamelCase

; Iterate through CamelCase words
(global-subword-mode 1)                           

Only use spaces

(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
(setq-default indicate-empty-lines nil)

Line wrap

(setq-default fill-column 78)
(global-visual-line-mode)
(setq line-move-visual t) ;; move via visual lines
(global-visual-fill-column-mode)

Visual replace

This is the good old search and replace as opposed to the fancy alternatives such as iedit and multiple cursors. You search for a word in the buffer/region, type in the replacement and confirm each one by pressing y or n or just press ! to apply this to everything.

(use-package visual-regexp
  :commands (vr/query-replace)
  :config
  (use-package visual-regexp-steroids
    :commands (vr/select-query-replace)))

Show Matching Brackets

Show matching brackets, parenthesis, etc.

(show-paren-mode t)
(setq show-paren-delay 0) 

Turn off the blinking cursor

(blink-cursor-mode 0)

UTF-8 please

(setq locale-coding-system 'utf-8) 
(set-terminal-coding-system 'utf-8) 
(set-keyboard-coding-system 'utf-8) 
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8) 

Warnings

No bells and no visible “bell” either!

(setq visible-bell nil) ;; The default
(setq ring-bell-function 'ignore)
;; Silence warnings generated by a function's being redefine by =defadvice=.
(setq ad-redefinition-action 'accept)

Yes or No

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

Backups

(let ((backup-dir (concat cpm-cache-dir "backup")))
  ;; Move backup file to `~/.emacs.d/.cache/backup'
  (setq backup-directory-alist `(("." . ,backup-dir)))
  ;; Makesure backup directory exist
  (when (not (file-exists-p backup-dir))
    (make-directory backup-dir t)))

(setq make-backup-files t               ; backup of a file the first time it is saved.
      backup-by-copying t               ; don't clobber symlinks
      version-control t                 ; version numbers for backup files
      delete-old-versions t             ; delete excess backup files silently
      delete-by-moving-to-trash t
      kept-old-versions 6               ; oldest versions to keep when a new numbered backup is made
      kept-new-versions 6               ; newest versions to keep when a new numbered backup is made
      )
(setq vc-make-backup-files t) ;;  backup versioned files, which Emacs does not do by default

Backup Walker

Traverse backups with backup-walker

(use-package backup-walker
  :commands backup-walker-start)

Auto Save

I make sure Emacs auto-saves often but the result is that it messes up my file tree. So, let’s ask Emacs to store its backups in the cache directory.

(setq auto-save-list-file-prefix
      (concat cpm-cache-dir "auto-save-list/.saves-"))

(setq auto-save-default t               ; auto-save every buffer that visits a file
      auto-save-timeout 30              ; number of seconds idle time before auto-save (default: 30)
      auto-save-interval 300            ; number of keystrokes between auto-saves (default: 300)
      auto-save-visited-file-name nil
      delete-auto-save-files t
      create-lockfiles nil)

Full Auto Save

I also make emacs just outright save all buffers.

(defun full-auto-save ()
  (interactive)
  (save-excursion
    (dolist (buf (buffer-list))
      (set-buffer buf)
      (if (and (buffer-file-name) (buffer-modified-p))
          (basic-save-buffer)))))
(add-hook 'auto-save-hook 'full-auto-save)

Custom file

Set up the customize file to its own separate file, instead of saving customize settings in init.el.

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file))

Desktop save

Save your frame/window/buffer config

(setq desktop-dirname             (concat cpm-cache-dir "desktops")
      desktop-base-file-name      "emacs.desktop"
      desktop-base-lock-name      "lock"
      desktop-path                (list desktop-dirname)
      desktop-save                'ask-if-new
      desktop-files-not-to-save   (concat "^$" ".*magit$")
      desktop-restore-eager 4
      desktop-load-locked-desktop t)

(when (not (file-exists-p desktop-dirname))
(make-directory desktop-dirname t))

(setq desktop-buffers-not-to-save
        (concat "\\("
                "^nn\\.a[0-9]+\\|\\.log\\|(ftp)\\|^tags\\|^TAGS"
                "\\|\\.emacs.*\\|\\.diary\\|\\.newsrc-dribble\\|\\.bbdb"
	          "\\)$"))

(desktop-save-mode 0)

(defun cpm/my-desktop ()
  "Load the desktop and enable autosaving"
  (interactive)
  (let ((desktop-load-locked-desktop "ask"))
    (desktop-read)
    (desktop-save-mode 1)))

(defun cpm/save-desktop-save-buffers-kill-emacs ()
  "Save buffers and current desktop every time when quitting emacs."
  (interactive)
  (desktop-save-in-desktop-dir)
  (save-buffers-kill-emacs))

Mac/OSX

There is some configuration to do when running Emacs on OS X (hence the “darwin” system-type check).

First we can define some general system checks

(setq IS-LINUX (eq system-type 'gnu/linux)
      IS-MAC (eq system-type 'darwin))
  (when IS-MAC
    ;; make fonts look better with anti-aliasing
    (setq mac-allow-anti-aliasing t)
    ;; delete files by moving them to the trash
    (setq delete-by-moving-to-trash t)
    (setq trash-directory "~/.Trash")

    ;; Don't make new frames when opening a new file with Emacs
    (setq ns-pop-up-frames nil)

    ;; fullscreen (disable for non-space full screen)
    (setq ns-use-native-fullscreen t)

    ;; disable emacs-mac smooth scrolling because it is seriously janky
    (setq mac-mouse-wheel-smooth-scroll nil)

    ;; Set modifier keys
    (setq mac-option-modifier 'meta) ;; Bind meta to ALT
    (setq mac-command-modifier 'super) ;; Bind apple/command to super if you want
    (setq mac-function-modifier 'hyper) ;; Bind function key to hyper if you want 
    (setq mac-right-option-modifier 'none) ;; unbind right key for accented input

    ;; Make forward delete work 
    (global-set-key (kbd "<H-backspace>") 'delete-forward-char)

    ;; Keybindings
    (global-set-key (kbd "s-q") 'save-buffers-kill-terminal)
    (global-set-key (kbd "s-v") 'yank)
    (global-set-key (kbd "s-c") 'evil-yank)
    (global-set-key (kbd "s-a") 'mark-whole-buffer)
    (global-set-key (kbd "s-x") 'kill-region)
    (global-set-key (kbd "s-w") 'delete-window)
    (global-set-key (kbd "s-W") 'delete-frame)
    (global-set-key (kbd "s-n") 'make-frame)
    (global-set-key (kbd "s-N") 'nameframe-create-frame)
    (global-set-key (kbd "s-z") 'undo-tree-undo)
    (global-set-key (kbd "s-s")
                    (lambda ()
                      (interactive)
                      (call-interactively (key-binding "\C-x\C-s"))))
    (global-set-key (kbd "s-Z") 'undo-tree-redo)
    (global-set-key (kbd "C-s-f") 'toggle-frame-fullscreen)
    ;; Emacs sometimes registers C-s-f as this weird keycode
    (global-set-key (kbd "<C-s-268632070>") 'toggle-frame-fullscreen)
)

    (defun open-dir-in-iterm ()
      "Open the current directory of the buffer in iTerm."
      (interactive)
      (let* ((iterm-app-path "/Applications/iTerm.app")
             (iterm-brew-path "/opt/homebrew-cask/Caskroom/iterm2/2.1.4/iTerm.app")
             (iterm-path (if (file-directory-p iterm-app-path)
                             iterm-app-path
                           iterm-brew-path)))
        (shell-command (concat "open -a " iterm-path " ."))))
        (global-set-key (kbd "C-x t") 'open-dir-in-iterm)

    ;; Not going to use these commands
    (put 'ns-print-buffer 'disabled t)
    (put 'suspend-frame 'disabled t)

    ;; -- This is for TextExpander
    ;; (setq ns-alternate-modifier 'alt)
    ;; (define-key global-map [(alt ?v)] 'scroll-down)
    ;; (define-key global-map [(meta ?v)] 'yank)

Time and Date Stamps

Emacs buffer timestamp settings

(setq 
  time-stamp-active t          ; do enable time-stamps
  time-stamp-line-limit 10     ; check first 10 buffer lines for Time-stamp: 
  time-stamp-format "Last modified on %02m-%02d-%04y %02H:%02M:%02S (%U)") ; date format
(add-hook 'write-file-hooks 'time-stamp) ; update when saving

Insert time or date

The code below sets the correct value for system-time-locale, and binds keys for insert-date/long and insert-date/short. Courtesy of emacs-hacks.

  (defun format-date (format)
  (let ((system-time-locale "en_US.UTF-8"))
    (insert (format-time-string format))))

(defun insert-date ()
  (interactive)
  (format-date "%A, %B %d %Y"))

(defun insert-date-and-time ()
  (interactive)
  (format-date "%m-%d-%Y %H:%M:%S"))

Location

(I only need this if I’m using circadian, which I’m not) Make Emacs watch and respond to changes in geographical location on OS X

(use-package osx-location
  :if (eq system-type 'darwin)
  :defer 10
  :commands osx-location-watch
  :config
  (osx-location-watch)
  (add-hook 'osx-location-changed-hook
               (lambda ()
                 (setq calendar-latitude osx-location-latitude
                       calendar-longitude osx-location-longitude
                       calendar-location-name (format "%s, %s" osx-location-latitude osx-location-longitude)))))

Core Packages

Modal Editing

General (Evil)

A convenient way to bind keys. Compatible with evil. For helpful discussion of setting up evil with general see this post.

(use-package general
  :demand t
  :config
  (general-override-mode)
  )

Vim Emulation

I’m coming from vim, and want modal keybidings in emacs. There are other, less radical ways of getting modal editing in emacs. For example, modalka is a nice package for modal editing (see also ryo-modal). But nothing beats full vim keybindings. And that is what evil is for. Install, automatically load, and enable evil. It’s like vim, but better!

Evil Mode

(use-package evil
  :demand t
  :init 
  (setq evil-want-integration nil)
  :config
  (progn
  ;; Cursor shape and color
    (defcustom dotemacs-evil/emacs-cursor
    "red"
    "The color of the cursor when in Emacs state."
    :type 'color
    :group 'dotemacs-evil)

    (defcustom dotemacs-evil/emacs-insert-mode
    nil
    "If non-nil, insert mode will act as Emacs state."
    :type 'boolean
    :group 'dotemacs-evil)

    ;; move over visual lines like normal lines
    (general-define-key :states '(motion normal)
           "j"   #'evil-next-visual-line
           "k"   #'evil-previous-visual-line)

    (setq evil-search-module 'evil-search)
    (setq evil-magic 'very-magic)
    ;; (setq evil-want-C-i-jump nil)
    ;; Set colors for cursor states
    (setq evil-emacs-state-cursor '("SkyBlue2" box))
    (setq evil-normal-state-cursor '("DarkGoldenrod2" box))
    (setq evil-visual-state-cursor '("gray" box)) 
    (setq evil-insert-state-cursor '("chartreuse3" (bar . 2)))
    (setq evil-replace-state-cursor '("red" hbar))
    (setq evil-motion-state-cursor  '("plum3" box))
    (setq evil-operator-state-cursor '("red" hollow))
    ;; (setq evil-visual-state-tag "VISUAL")
    ;use insert in commits automatically 
    (add-hook 'git-commit-mode-hook 'evil-insert-state)
    (evil-set-initial-state 'messages-buffer-mode 'normal)
    (evil-set-initial-state 'magit-log-edit-mode 'insert)
    ;; evil-normal-state is preferred, so revert when idle
    ;; (run-with-idle-timer 60 t 'evil-normal-state)
    ;; don't echo evil state
    (setq evil-echo-state nil)
    ;; don't move cursor back when exiting insert state
    (setq evil-move-cursor-back nil)
    ;; evil everywhere
    (evil-mode 1))) 

Evil Related Packages & Settings

There are some other useful setup packages for evil

Evil Collection

A collection of keybindings for evil

(use-package evil-collection
  :ensure t
  :after evil
  :defer 1
  :config
  (evil-collection-init)
  (setq evil-collection-mode-list nil))
Evil indent
(use-package evil-indent-textobject :commands (evil-indent))
Change Cursor In Terminal
(defun my-send-string-to-terminal (string)
  (unless (display-graphic-p) (send-string-to-terminal string)))

(defun my-evil-terminal-cursor-change ()
  (when (string= (getenv "TERM_PROGRAM") "iTerm.app")
    (add-hook 'evil-insert-state-entry-hook (lambda () (my-send-string-to-terminal "\e]50;CursorShape=1\x7")))
    (add-hook 'evil-insert-state-exit-hook  (lambda () (my-send-string-to-terminal "\e]50;CursorShape=0\x7"))))
  (when (and (getenv "TMUX") (string= (getenv "TERM_PROGRAM") "iTerm.app"))
    (add-hook 'evil-insert-state-entry-hook (lambda () (my-send-string-to-terminal "\ePtmux;\e\e]50;CursorShape=1\x7\e\\")))
    (add-hook 'evil-insert-state-exit-hook  (lambda () (my-send-string-to-terminal "\ePtmux;\e\e]50;CursorShape=0\x7\e\\")))))

(add-hook 'after-make-frame-functions (lambda (frame) (my-evil-terminal-cursor-change)))
(my-evil-terminal-cursor-change)
Evil Surround Commands Like Vim-Surround
(use-package evil-surround
  :commands (evil-surround-region evil-surround-change evil-surround-delete)
  ;; :hook ((LaTeX-mode org-mode markdown-mode prog-mode) . evil-surround-mode)
  :general
  (:states '(visual)
  "s" 'evil-surround-region
  "S" 'evil-substitute)
  ;; :config (global-evil-surround-mode 1)
  )

(use-package embrace 
  :after evil-surround
  :demand t)
  ;; :hook ((LaTeX-mode . embrace-LaTeX-mode-hook)
  ;;        (org-mode . embrace-org-mode-hook)
  ;;        (org-mode . embrace-markdown-mode-hook)
  ;;        (markdown-mode . embrace-markdown-mode-hook)))

(use-package evil-embrace
 :after evil-surround
 :init
 (evil-embrace-enable-evil-surround-integration)
 :config
 (setq evil-embrace-show-help-p nil)
 (add-hook 'org-mode-hook 'embrace-org-mode-hook)
 (defun embrace-markdown-mode-hook ()
 (dolist (lst '((?* "*" . "*")  
                (?\ "\\" . "\\")
                (?$ "$" . "$")
                (?/ "/" . "/")))
  (embrace-add-pair (car lst) (cadr lst) (cddr lst))))
  (add-hook 'markdown-mode-hook 'embrace-markdown-mode-hook)
  )
Commenting
(use-package evil-commentary
  :commands (evil-commentary evil-commentary-line)
  ;; :diminish evil-commentary-mode
  :config
  (evil-commentary-mode))
Graphical undo
(use-package undo-tree
  :commands (undo-tree-undo undo-tree-redo undo-tree-visualize)
  :init
  (global-undo-tree-mode)
  (setq undo-tree-visualizer-timestamps t)
  (setq undo-tree-visualizer-diff t)
  ;; supposedly causes errors in undo read
  ;; see https://emacs.stackexchange.com/a/34214/11934
  (setq undo-tree-enable-undo-in-region nil)
  ;; stop littering - set undo directory 
  (let ((undo-dir (concat cpm-cache-dir "undo")))
    (setq undo-tree-history-directory-alist `(("." . ,undo-dir)))
    (unless (file-directory-p undo-dir)
      (make-directory undo-dir t)))
  (setq undo-tree-auto-save-history nil))
Evil Multiedit

A version of multiple cursors for use with evil. Courtesy of hlissner.

(use-package evil-multiedit
 :ensure t
 :after evil-visualstar
 :config
 ;; Default keybindings
 ;; Highlights all matches of the selection in the buffer.
(define-key evil-visual-state-map "R" 'evil-multiedit-match-all)

;; Match the word under cursor (i.e. make it an edit region). Consecutive presses will
;; incrementally add the next unmatched match.
(define-key evil-normal-state-map (kbd "M-d") 'evil-multiedit-match-and-next)
;; Match selected region.
(define-key evil-visual-state-map (kbd "M-d") 'evil-multiedit-and-next)
;; Insert marker at point
(define-key evil-insert-state-map (kbd "M-d") 'evil-multiedit-toggle-marker-here)

;; Same as M-d but in reverse.
(define-key evil-normal-state-map (kbd "M-D") 'evil-multiedit-match-and-prev)
(define-key evil-visual-state-map (kbd "M-D") 'evil-multiedit-and-prev)

;; OPTIONAL: If you prefer to grab symbols rather than words, use
;; `evil-multiedit-match-symbol-and-next` (or prev).

;; Restore the last group of multiedit regions.
(define-key evil-visual-state-map (kbd "C-M-D") 'evil-multiedit-restore)

;; RET will toggle the region under the cursor
(define-key evil-multiedit-state-map (kbd "RET") 'evil-multiedit-toggle-or-restrict-region)

;; ...and in visual mode, RET will disable all fields outside the selected region
(define-key evil-motion-state-map (kbd "RET") 'evil-multiedit-toggle-or-restrict-region)

;; For moving between edit regions
(define-key evil-multiedit-state-map (kbd "C-n") 'evil-multiedit-next)
(define-key evil-multiedit-state-map (kbd "C-p") 'evil-multiedit-prev)
(define-key evil-multiedit-insert-state-map (kbd "C-n") 'evil-multiedit-next)
(define-key evil-multiedit-insert-state-map (kbd "C-p") 'evil-multiedit-prev)

;; Ex command that allows you to invoke evil-multiedit with a regular expression, e.g.
(evil-ex-define-cmd "ie[dit]" 'evil-multiedit-ex-match)
)
Evil Multiple Cursors

Multiple cursors implementation for evil-mode

(use-package evil-mc
  :ensure t
  :commands (evil-mc-make-all-cursors evil-mc-make-and-goto-next-match))
Evil Numbers

Increment an decrement numbers

(use-package evil-numbers
  :commands (evil-numbers/inc-at-pt evil-numbers/dec-at-pt)
  :init
  (general-define-key
    :states '(normal visual insert emacs)
    "H-s" 'evil-numbers/inc-at-pt
    "H-a" 'evil-numbers/dec-at-pt))
Evil Visualstar

From bling: https://github.com/bling/evil-visualstar

(use-package evil-visualstar
  :commands (evil-visualstar/begin-search-forward evil-visualstar/begin-search-backward))

Appearance & UI

Various settings to make Emacs (mostly the GUI version) look better or make interaction smoother.

Appearance

Font

A good fixed-width or monospaced font is important. Inconsolata is a nice monospaced font. I’ve used a version, Inconsolata-LGC, that also has bold and italic fonts. However, I’m currently using hasklig, which is a fork of Source Code Pro with ligatures added.

To install a font on OS X, you can use Homebrew with Homebrew Cask.

# You may need to run these two lines if you haven't set up Homebrew
# Cask and its fonts formula.
brew install caskroom/cask/brew-cask
brew tap caskroom/fonts
brew cask install font-inconsolata-lgc

Here I set the variable values, including a variable font face for themes that use that for headings, such as solarized. The settings are called below.

(defvar cpm-font (font-spec :family "Hasklig" :size 13))
(defvar cpm-vari-font (font-spec :family "Avenir"))
(defvar cpm-unicode-font (font-spec :family "STIXGeneral"))

Here we tell emacs to use the fonts set in the variables above.

(set-face-attribute 'default nil :font cpm-font)
(set-face-attribute 'variable-pitch nil :font cpm-vari-font)
(set-fontset-font t 'unicode cpm-unicode-font nil 'prepend)

Frame Title

Show the filepath in the frame title (disabled due to text color issues).

(setq frame-title-format '('nil))
  ;; (setq-default frame-title-format
  ;;           '((buffer-file-name "%f" "%b")))

Frame Defaults

I like the frame either centered and approximately 2/3 of a 13inch laptop screen or maximized.

(if (display-graphic-p)
  (progn
  ;; start frame of emacs maximized
  (add-to-list 'initial-frame-alist '(fullscreen . maximized))

  ;; new frames
  (setq default-frame-alist
            '(
              (top . 25)
              (left . 275)
              (width . 106) ;; chars
              (height . 60) ;; lines
              ))))

Transparent frame titlebar

;; https://github.com/d12frosted/homebrew-emacs-plus/blob/master/Formula/emacs-plus.rb#L98
;; https://github.com/d12frosted/homebrew-emacs-plus/issues/55
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Properties-in-Mode.html#Properties-in-Mode
(when (memq window-system '(mac ns))
  (add-to-list 'default-frame-alist '(ns-appearance . dark))
  (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)))

Borderless Frame

;; (setq default-frame-alist '((undecorated . t)))

Get rid of UI cruft

Turn off all of the GUI cruft.

;; Turn off mouse interface early in startup to avoid momentary display
(when (display-graphic-p)
  (menu-bar-mode -1)
  (tool-bar-mode -1)
  (scroll-bar-mode -1)
  (tooltip-mode -1))

No menu bar in terminal

Ditto for the terminal.

(when (not (display-graphic-p))
  (menu-bar-mode -1))

Change Font Size

(when IS-MAC
  (global-set-key (kbd "s-=") 'scale-up-font)
  (global-set-key (kbd "s--") 'scale-down-font)
  (global-set-key (kbd "s-0") 'reset-font-size))

Native Line Numbers (Emacs 26)

Emacs now has native line number support in the C source code, rather than the other packages, which utilize elisp hacks, making it much faster.

(use-package line-numbers
  :ensure nil
  ;; :hook (markdown-mode prog-mode)
  :commands display-line-numbers-mode
  :init
  (setq-default display-line-numbers-type 'visual)) 

Highlight numbers

Highlight numbers in source code

(use-package highlight-numbers
  :defer t
  :init
  (add-hook 'prog-mode-hook #'highlight-numbers-mode))

Highlight TODOs

highlight TODO statements in comments

(use-package hl-todo
  :defer t
  :init
  ;; (add-hook 'org-mode-hook #'hl-todo-mode)
  (add-hook 'prog-mode-hook #'hl-todo-mode)
  (add-hook 'markdown-mode-hook #'hl-todo-mode))

All the icons

Like the title says…

(use-package all-the-icons
 :defer 1)
;;dependency
(use-package font-lock+
  :defer 1)

All the icons dired

;; icons for dired
(use-package all-the-icons-dired
  :defer t
  :init
  (add-hook 'dired-mode-hook 'all-the-icons-dired-mode))

Beacon

Useful for letting you know where the cursor is

(use-package beacon
  :defer 10
  :config
  (beacon-mode 1)
  (add-to-list 'beacon-dont-blink-major-modes 'eshell-mode))

Emoji

Add emoji support. This is useful when working with html.

(use-package emojify
 :commands (emojify-mode emojify-apropos-emoji)
 ;; :hook ((prog-mode markdown-mode) . emojify-mode)
 :config
 (setq emojify-emojis-dir (concat cpm-etc-dir "emojis")))

Theme

Toggle OSX Menubar Dark Mode

A dark mode toggle for osx menubar.

(defun cpm/osx-toggle-menubar-theme ()
  (interactive)
  (shell-command "dark-mode"))
(defun cpm/osx-menubar-theme-light ()
  (interactive)
  (shell-command "dark-mode off"))
(defun cpm/osx-menubar-theme-dark ()
  (interactive)
  (shell-command "dark-mode on"))
Solarized

The best low-contrast theme out there.

(use-package solarized-theme
  :if (display-graphic-p)
  :init
    (progn
    (setq org-todo-keyword-faces
         '(("TODO" . (:foreground "orange" :weight bold)) ("STARTED" . "yellow")
           ("WAITING" . (:weight bold))
           ("SUBMITTED-C" . "green") ("SUBMITTED-J" . "green")
           ("ACCEPTED-C" . "silver") ("ACCEPTED-J" . "silver")
           ("REVISE" . (:foreground "violet" :weight bold))))

        ;; don't make the fringe stand out from the background
        (setq solarized-distinct-fringe-background nil)

        ;; change the font for some headings and titles
        (setq solarized-use-variable-pitch t)

        ;; make the modeline high contrast
        (setq solarized-high-contrast-mode-line nil)
        ;; use this setting without hi contrast modeline
        (setq x-underline-at-descent-line t)

        ;; Use bolding
        (setq solarized-use-less-bold nil)

        ;; Use more italics
        (setq solarized-use-more-italic t)

        ;; Use colors for indicators such as git:gutter, flycheck and similar
        (setq solarized-emphasize-indicators t)

        ;; Set to nil of you don't want to change size of org-mode headlines (but keep other size-changes)
        (setq solarized-scale-org-headlines t)

        ;; Theme & menubar toggle
        (setq active-theme 'solarized-dark)
        (defun toggle-dark-light-theme ()
        (interactive)
        (if (eq active-theme 'solarized-light) 
            (progn (setq active-theme 'solarized-dark) 
                   (cpm/osx-menubar-theme-dark)
                   (add-to-list 'default-frame-alist '(ns-appearance . dark))
                   (force-mode-line-update))
            (progn (setq active-theme 'solarized-light) 
                   (cpm/osx-menubar-theme-light)
                   (add-to-list 'default-frame-alist '(ns-appearance . light))
                   (force-mode-line-update)))
        (load-theme active-theme)))
        ;; (powerline-reset)))

        ;; Change the lettering color dependent on solarized theme
        (defun cpm/frame-title-color ()
        (if (eq active-theme 'solarized-light)
            (progn
              (add-to-list 'default-frame-alist '(ns-appearance . light))
              (force-mode-line-update))
          (progn
          (add-to-list 'default-frame-alist '(ns-appearance . dark))
          (force-mode-line-update))))
        (add-hook! (before-make-frame-hook nameframe-make-frame-hook) 'cpm/frame-title-color)

         (progn
         (defvar after-load-theme-hook nil
         "Hook run after a color theme is loaded using `load-theme'.")
         (defadvice load-theme (after run-after-load-theme-hook activate)
         "Run `after-load-theme-hook'."
         (run-hooks 'after-load-theme-hook))
         (defun customize-solarized-dark ()
         "Customize solarized theme"
         (if (member 'solarized-dark custom-enabled-themes)
               (custom-theme-set-faces
               'solarized-dark
               ;; make bg darker for higher contrast
               ;; '(default ((t (:inherit nil :stipple nil :background "#002833" :foreground "#839496" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 130 :width normal :foundry "nil" :family "Inconsolata LGC"))))
               '(default ((t (:background "#002833" :foreground "#839496"))))
               ;; matching fringe
               '(fringe ((t (:background "#002833" :foreground "#586e75"))))
               ;; fix modeline underline
                '(mode-line ((t (:background "#073642" :foreground "#839496" :box (:line-width 1 :color "#073642" :style unspecified) :overline "#073642" :underline "#073642"))))
               ;; terminal
               '(term ((t (:background "#002833" :foreground "#839496"))))
               ;; org faces
               '(org-block ((t (:foreground "#2E8B57"))))
               '(org-block-begin-line ((t (:foreground "#74a8a4" :weight bold :slant normal))))
               '(org-block-end-line ((t (:foreground "#74a8a4" :weight bold :slant normal))))
               '(org-level-1 ((t (:inherit variable-pitch :foreground "#268bd2" :height 1.5))))
               '(org-level-2 ((t (:inherit variable-pitch :foreground "medium sea green" :height 1.3))))
               '(org-level-3 ((t (:inherit variable-pitch :foreground "#cb4b16" :height 1.2))))
               '(org-level-4 ((t (:inherit variable-pitch :foreground "#6c71c4" :height 1.15))))
               '(org-level-8 ((t (:inherit variable-pitch :foreground "#9e1e86" :height 1.1))))
               '(org-quote ((t (:inherit org-block :slant normal :weight normal))))
               ;; markdown faces
               '(markdown-comment-face ((t (:weight normal :slant italic :strike-through nil))))
               '(markdown-header-face-1 ((t (:inherit variable-pitch :foreground "#268bd2" :height 1.75))))
               '(markdown-header-face-2 ((t (:inherit variable-pitch :foreground "medium sea green" :height 1.45))))
               '(markdown-header-face-3 ((t (:inherit variable-pitch :foreground "#cb4b16" :height 1.2))))
               ;; helm faces
               '(helm-selection ((t (:background "#073642" :foreground "goldenrod1" :underline nil))))
               '(helm-match ((t (:foreground "#b58900"))))
               ;; line number highlighting 
               '(line-number-current-line ((t (:inherit default :foreground "goldenrod1"))))
               ;; '(nlinum-current-line ((t (:inherit default :foreground "goldenrod1"))))
               '(linum-highlight-face ((t (:inherit default :foreground "goldenrod1"))))
                ;; '(nlinum-hl-face ((t (:inherit default :foreground "goldenrod1"))))
               ;; battery faces
               '(fancy-battery-charging ((t (:foreground "dark blue" :weight bold))))
               '(fancy-battery-critical ((t (:foreground "dark red" :weight bold))))
               '(fancy-battery-discharging ((t (:foreground "dark magenta" :weight bold)))))))

          (add-hook 'after-load-theme-hook 'customize-solarized-dark)

     (defun customize-solarized-light ()
     "Customize solarized theme"
     (if (member 'solarized-light custom-enabled-themes)
           (custom-theme-set-faces
           'solarized-light
           ;; org faces
           '(org-block ((t (:foreground "#2E8B57"))))
           '(org-block-begin-line ((t (:foreground "#74a8a4" :weight bold :slant normal))))
           '(org-block-end-line ((t (:foreground "#74a8a4" :weight bold :slant normal))))
           '(org-level-1 ((t (:inherit variable-pitch :foreground "#268bd2" :height 1.3))))
           '(org-level-2 ((t (:inherit variable-pitch :foreground "medium sea green" :height 1.2))))
           '(org-level-3 ((t (:inherit variable-pitch :foreground "#cb4b16" :height 1.15))))
           '(org-level-4 ((t (:inherit variable-pitch :foreground "#6c71c4" :height 1.15))))
           '(org-level-8 ((t (:inherit variable-pitch :foreground "#9e1e86" :height 1.1))))
           '(org-quote ((t (:inherit org-block :slant normal :weight normal))))
           ;; markdown faces
           '(markdown-comment-face ((t (:weight normal :slant italic :strike-through nil))))
           '(markdown-header-face-1 ((t (:inherit variable-pitch :foreground "#268bd2" :height 1.75))))
           '(markdown-header-face-2 ((t (:inherit variable-pitch :foreground "medium sea green" :height 1.45))))
           '(markdown-header-face-3 ((t (:inherit variable-pitch :foreground "#cb4b16" :height 1.2))))

           ;; helm faces
           '(helm-selection ((t (:background "#eee8d5" :foreground "#268bd2" :underline nil :weight bold))))
           '(helm-match ((t (:foreground "#cb4b16" :weight bold))))

           ;; '(helm-selection ((t (:foreground "#f7f438" :background "#64b5ea" :underline nil :weight bold))))
           ;; line size 
           '(set-face-attribute 'linum nil :inherit 'fixed-pitch)
           ;; line highlighting 
           '(linum-highlight-face ((t (:inherit default :foreground "#002b36"))))
           ;; '(nlinum-hl-face ((t (:inherit default :foreground "#002b36"))))
           '(line-number-current-line ((t (:inherit default :foreground "#002b36"))))
           ;; '(nlinum-current-line ((t (:inherit default :foreground "#002b36"))))
           ;; battery faces
           '(fancy-battery-charging ((t (:foreground "dark blue" :weight bold))))
           '(fancy-battery-critical ((t (:foreground "dark red" :weight bold))))
           '(fancy-battery-discharging ((t (:foreground "dark magenta" :weight bold))))))
               )

          (add-hook 'after-load-theme-hook 'customize-solarized-light))
          (load-theme 'solarized-dark t))
Gruvbox

This is a great general-purpose theme. Use it in terminal.

(use-package gruvbox-theme
  :if (not (display-graphic-p))
  :init
  (load-theme 'gruvbox t)
  )
Other Themes

Make sure that other themes I like are downloaded and available (not that I use anything other than solarized :)

(defvar packages-appearance '(doom-themes nord-theme solarized-theme
  zenburn-theme molokai-theme darktooth-theme gotham-theme
  ample-theme material-theme leuven-theme
  spacemacs-theme gruvbox-theme forest-blue-theme flatland-theme
  afternoon-theme cyberpunk-theme darkmine-theme
  tao-theme darkokai-theme jazz-theme suscolors-theme
  omtose-phellack-theme atom-one-dark-theme nubox
  color-theme-sanityinc-tomorrow alect-themes kaolin-themes)
"A list of themes to ensure are installed at launch.")

(defun appearance-packages-installed-p ()
  (loop for p in packages-appearance
        when (not (package-installed-p p)) do (return nil)
        finally (return t)))

(unless (appearance-packages-installed-p)
  ;; check for new packages (package versions)
  (message "%s" "Emacs is now refreshing its package themes...")
  (package-refresh-contents)
  (message "%s" " done.")
  ;; install the missing packages
  (dolist (p packages-appearance)
    (when (not (package-installed-p p))
      (package-install p))))

(provide 'packages-appearance)

Modeline

Hide mode line

Hide mode line. From http://bzg.fr/emacs-hide-mode-line.html

(defvar-local hidden-mode-line-mode nil)
(defvar-local hide-mode-line nil)
(define-minor-mode hidden-mode-line-mode
  "Minor mode to hide the mode-line in the current buffer."
  :init-value nil
  :global t
  :variable hidden-mode-line-mode
  :group 'editing-basics
  (if hidden-mode-line-mode
      (setq hide-mode-line mode-line-format
            mode-line-format nil)
    (setq mode-line-format hide-mode-line
          hide-mode-line nil))
  (force-mode-line-update)
  ;; Apparently force-mode-line-update is not always enough to
  ;; redisplay the mode-line
  (redraw-display)
  (when (and (called-interactively-p 'interactive)
             hidden-mode-line-mode)
    (run-with-idle-timer
     0 nil 'message
     (concat "Hidden Mode Line Mode enabled.  "
             "Use M-x hidden-mode-line-mode to make the mode-line appear."))))
Doom Modeline
  (use-package doom-modeline
    :hook (after-init . doom-modeline-init)
    :config
    (setq doom-modeline-bar-width 3
          doom-modeline-height 38)

     ;; Change the evil tag 
     (setq evil-normal-state-tag   (propertize " 🅝 " )
           evil-emacs-state-tag    (propertize " 🅔 " )
           evil-insert-state-tag   (propertize " 🅘 " )
           evil-replace-state-tag  (propertize " 🅡 " )
           evil-motion-state-tag   (propertize " 🅜 " )
           evil-visual-state-tag   (propertize " 🅥 " )
           evil-operator-state-tag (propertize " 🅞 " ))

     (doom-modeline-def-segment evil-state
     "The current evil state. Requires `evil-mode' to be enabled."
     (when (bound-and-true-p evil-local-mode)
       (let ((tag (evil-state-property evil-state :tag t)))
         (propertize tag 'face
                (if (doom-modeline--active)
                    (cond ((eq tag evil-normal-state-tag)   '(:foreground "DarkGoldenrod2"))
                          ((eq tag evil-emacs-state-tag)    '(:foreground "SkyBlue2"))
                          ((eq tag evil-insert-state-tag)   '(:foreground "chartreuse3"))
                          ((eq tag evil-motion-state-tag)   '(:foreground "plum3"))
                          ((eq tag evil-replace-state-tag)  '(:foreground "red"))
                          ((eq tag evil-visual-state-tag)   '(:foreground "gray"))
                          ((eq tag evil-operator-state-tag) '(:foreground "red"))))))))


    ;; window number faces & formatting
    (doom-modeline-def-segment window-number
      (if (bound-and-true-p window-numbering-mode)
          (propertize (format " %s " (window-numbering-get-number-string))
                      'face (if (doom-modeline--active)
                                'doom-modeline-active-window-number
                              'doom-modeline-inactive-window-number))
     ""))
    ;; workspace number faces & formatting
    (doom-modeline-def-segment workspace-number
      "The current workspace name or number. Requires `eyebrowse-mode' to be
enabled."
      (if (and (bound-and-true-p eyebrowse-mode)
               (< 1 (length (eyebrowse--get 'window-configs))))
          (let* ((num (eyebrowse--get 'current-slot))
                 (tag (when num (nth 2 (assoc num (eyebrowse--get 'window-configs)))))
                 (str (if (and tag (< 0 (length tag)))
                          tag
                        (when num (int-to-string num)))))
            (propertize (format " %s |" str) 'face 'doom-modeline-highlight))
        ""))


     (defface doom-modeline-active-window-number
     '((t (:inherit warning)))
     "Face for active window number segment of the mode-line."
     :group 'doom-modeline)
     (defface doom-modeline-inactive-window-number
     '((t (:inherit mode-line-emphasis)))
     "Face for inactive window number segment of the mode-line."
     :group 'doom-modeline)

    :custom-face
    (doom-modeline-eyebrowse ((t (:inherit highlight))))
    (doom-modeline-bar ((t (:inherit highlight :inverse-video t :background "#268bd2"))))
    (doom-modeline-inactive-bar ((t (:inherit highlight)))))

Calender Settings

Settings for using org with calander entries, with help from Adolfo Villafiorita.

(setq diary-file (concat cpm-local-dir "diary-files/diary"))
(setq diary-location (concat cpm-local-dir "diary-files/"))
(setq org-agenda-include-diary t)
(setq diary-display-function 'diary-fancy-display)
(add-hook 'diary-list-entries-hook 'diary-include-other-diary-files)
(add-hook 'diary-list-entries-hook 'diary-sort-entries t)

; calendars you want to download
; each item links to a remote iCal calendar
(setq calendars
      '(("work" . "https://p03-calendarws.icloud.com/ca/subscribe/1/tIT1AJEoqkLRxp8CxCHhXyjFoeUBDYfAOUw0oyxmqy21qQl2olDRxHslZ4QNxavP")
        ("home" . "https://p03-calendarws.icloud.com/ca/subscribe/1/BTg33u3i0rudppklqMA7T09Ynq1HyJEjmw-rnTsbE1B8Nz5NEXd7RtG_xRtpgp54")
        ("family" . "https://p03-calendarws.icloud.com/ca/subscribe/1/dns1xyaQvy2VnPHiHQQngVwdr7dNGlaWzp2Q3e_UdjCs-vkJ-8TEl_QU9Fyhm_-5BSPmwSHOtdoqgJi11kJgcpu2AYlReZuNXGpGFBhziq0")
        ))

(defun cpm--getcal (url file)
  "Download ics file and add it to file"
  (let ((tmpfile (url-file-local-copy url)))
    (icalendar-import-file tmpfile file)
    (kill-buffer (car (last (split-string tmpfile "/"))))))

(defun cpm/getcals ()
  "Load a set of ics calendars into emacs diary files"
  (interactive)
  (mapcar #'(lambda (x)
              (let ((file (concat diary-location (car x)))
                    (url (cdr x)))
                (message (concat "Loading " url " into " file))
                (find-file file)
                ;; (flush-lines "^[& ]") ;; if you import ical as non marking
                (erase-buffer) ;; to avoid duplicating events
                (cpm--getcal url file)
                ))
          calendars)
   ;; send everything to a diary file
   (shell-command-to-string "cat work family home > diary"))

Completion

Company

(use-package company
  :ensure t
  :hook ((prog-mode text-mode) . company-mode)
  :custom-face
  ;; Nicer looking faces
  (company-tooltip-common
    ((t (:inherit company-tooltip :weight bold :underline nil))))
  (company-tooltip-common-selection
    ((t (:inherit company-tooltip-selection :weight bold :underline nil))))
  :init
  (setq company-idle-delay 0.45
        company-minimum-prefix-length 3
        company-require-match nil
        company-dabbrev-ignore-case nil
        company-dabbrev-downcase nil)
  :config
  ;; Default backends
  (add-to-list 'company-backends 'company-files)
  ;; key bindings
  (let ((map company-active-map))
   (define-key map (kbd "TAB") 'company-complete-selection)
   (define-key map (kbd "C-/") 'company-search-candidates)
   (define-key map (kbd "C-M-/") 'company-filter-candidates)
   (define-key map (kbd "C-d") 'company-show-doc-buffer)
   (define-key map (kbd "C-j") 'company-select-next)
   (define-key map (kbd "C-k") 'company-select-previous)
   (define-key map (kbd "C-l") 'company-complete-selection))
)

Company-Bibtex

(use-package company-bibtex
  :ensure t
  :after company
  :config
  (setq company-bibtex-bibliography "~/Dropbox/Work/All.bib")
  (setq company-bibtex-org-citation-regex "-?@")
  (add-to-list 'company-backends 'company-bibtex))

Yasnippet

A template system for Emacs http://joaotavora.github.com/yasnippet/

(use-package yasnippet
  :hook ((prog-mode text-mode) . yas-minor-mode)
  :commands (yas-expand yas-minor-mode)
  :diminish (yas-minor-mode . "")
  :config
  ;; see https://emacs.stackexchange.com/a/30150/11934
  (defun cpm/yas-org-mode-hook ()
    (setq-local yas-buffer-local-condition
            '(not (org-in-src-block-p t))))
  (add-hook 'org-mode-hook #'cpm/yas-org-mode-hook)

  ;; snippet directory
  (setq yas-snippet-dirs '("~/.emacs.d/.local/snippets/cpm-snippets"
                           yasnippet-snippets-dir))
  ;; the official snippet collection https://github.com/AndreaCrotti/yasnippet-snippets
  (use-package yasnippet-snippets :ensure t :after yasnippet :demand t)

  ;; Adding yasnippet support to company
  (with-eval-after-load 'company-mode
  (add-to-list 'company-backends '(company-yasnippet)))
  (yas-reload-all))

Navigation

Bookmarks

Use Bookmark Plus. Since it is an emacs wiki package you can’t get it from MELPA. The git mirror is here.

(use-package bookmark+
  :commands (bmkp-switch-bookmark-file-create bmkp-set-desktop-bookmark)
  :config
  (setq bookmark-default-file (concat cpm-cache-dir "bookmarks"))
  (setq bmkp-last-as-first-bookmark-file (concat cpm-cache-dir "bookmarks"))
)

Dired

(use-package dired
  :ensure nil
  :commands (dired dired-jump dired-jump-other-window)
  :general
  (:keymaps 'dired-mode-map
   :states '(normal motion)
   "l" #'dired-find-file
   "h" #'dired-up-directory
   "q" #'quit-window)
  :config
  (setq insert-directory-program "gls" dired-use-ls-dired t)
  ;; list directories first
  ;; (setq dired-listing-switches "-al --group-directories-first")
  (setq dired-listing-switches "-laGh1v --group-directories-first")
  ;; don't ask about killing buffer visiting file
  (setq dired-clean-confirm-killing-deleted-buffers nil)
  ;; stop asking about recurisve actions
  (setq dired-recursive-copies 'always)
  (setq dired-recursive-deletes 'always))

Dired Extensions

Dired Plus

Add extra functionality to dired. Not quite sure about this one yet.

(use-package dired+
  :ensure t
  :after dired
  :init
  (setq font-lock-maximum-decoration nil)
  (setq diredp-hide-details-initially-flag nil)
  (diredp-toggle-find-file-reuse-dir 1))
Peep Dired

Quicklook-like extension for dired

(use-package peep-dired
  :ensure t
  :commands (peep-dired)
  :general
  (:keymaps 'dired-mode-map
   :states '(normal motion)
   "p" #'peep-dired)
  (:keymaps 'peep-dired-mode-map
   :states '(normal)
   "j" #'peep-dired-next-file
   "k" #'peep-dired-prev-file)
  :config
  (add-hook 'peep-dired-hook 'evil-normalize-keymaps)
  (setq peep-dired-ignored-extensions '("mkv" "iso" "mp4" "pdf" "gif")
        peep-dired-max-size 5242880))
Dired Copy Large Files & Directories

Lets you copy huge files and directories without Emacs freezing up and with convenient progress bar updates. Courtesy of Or Emacs.

;;;###autoload
(defun ora-dired-rsync (dest)
  (interactive
   (list
    (expand-file-name
     (read-file-name
      "Rsync to:"
      (dired-dwim-target-directory)))))
  ;; store all selected files into "files" list
  (let ((files (dired-get-marked-files
                nil current-prefix-arg))
        ;; the rsync command
        (tmtxt/rsync-command
         "rsync -arvz --progress "))
    ;; add all selected file names as arguments
    ;; to the rsync command
    (dolist (file files)
      (setq tmtxt/rsync-command
            (concat tmtxt/rsync-command
                    (shell-quote-argument file)
                    " ")))
    ;; append the destination
    (setq tmtxt/rsync-command
          (concat tmtxt/rsync-command
                  (shell-quote-argument dest)))
    ;; run the async shell command
    (async-shell-command tmtxt/rsync-command "*rsync*")
    ;; finally, switch to that window
    (other-window 1)))

Helm

Helm is a robust and well-designed completion framework. It can do quite a lot.

Helm Settings

(use-package helm
  :general
  ("M-x" 'helm-M-x)
  ("C-h i" 'helm-info)
  :diminish (helm-mode . "")
  :commands (helm-mini helm-M-x helm-find-files helm-find)
  :config
  (progn
    ;; Use helm to provide :ls, unless ibuffer is used
    (evil-ex-define-cmd "buffers" 'helm-buffers-list)
    (set-face-attribute 'helm-source-header nil
    :height 180)
    (setq helm-locate-fuzzy-match nil
          helm-locate-command "mdfind -interpret -name %s %s")
    (setq helm-M-x-fuzzy-match t  ;; Use fuzzy match in helm
          helm-apropos-fuzzy-match t
          helm-buffers-fuzzy-matching t
          helm-imenu-fuzzy-match t
          helm-recentf-fuzzy-match t
          helm-adaptive-mode 1 ; learn from selections
          helm-prevent-escaping-from-minibuffer t
          helm-bookmark-show-location t
          helm-ff-file-name-history-use-recentf t
          helm-find-files-sort-directories t
          helm-display-header-line nil
          helm-move-to-line-cycle-in-source t
          helm-always-two-windows t
          helm-split-window-in-side-p nil
          ;; helm-split-window-default-side 'other
          helm-echo-input-in-header-line t)
    (setq helm-boring-buffer-regexp-list
          (quote
           ("\\Minibuf.+\\*"
            "\\` "
            "\\*.+\\*"
            )))
    (setq helm-white-buffer-regexp-list
          (quote
           ("\\*magit:"
            "\\*eshell"
            "\\*ansi-term"
            )))
    (helm-autoresize-mode 1)
    (setq helm-autoresize-max-height 40)
    (setq helm-autoresize-min-height 35)
    ;; (define-key helm-map (kbd "C-a") (kbd "RET"))
    ;;; helm vim-bindings in buffer ;;
    (map! (:map helm-map
      "TAB"   'helm-execute-persistent-action ; rebind tab to do persistent action
      "C-i"   'helm-execute-persistent-action ; make TAB works in terminal
      "C-z"   'helm-select-action ; list actions using C-z
      "C-j"   'helm-next-line
      "C-k"   'helm-previous-line
      "C-h"   'helm-next-source
      "C-l"   'helm-previous-source
      "C-S-h" 'describe-key)))
    (helm-mode 1))

;; use helm follow mode for search
(with-eval-after-load 'helm-regexp
(setq helm-source-occur
      (helm-make-source "Occur" 'helm-source-multi-occur
        :follow 1)))

Hide Mode Lines in Helm

Hide modelines of other windows while helm is open, again from https://github.com/hatschipuh/better-helm.

 (defvar my-helm-bottom-buffers nil
	"List of bottom buffers before helm session.
	Its element is a pair of `buffer-name' and `mode-line-format'.")

 (defun my-helm-bottom-buffers-init ()
	(setq-local mode-line-format (default-value 'mode-line-format))
	(setq my-helm-bottom-buffers
	  (cl-loop for w in (window-list)
		   when (window-at-side-p w 'bottom)
		   collect (with-current-buffer (window-buffer w)
				 (cons (buffer-name) mode-line-format)))))

 (defun my-helm-bottom-buffers-hide-mode-line ()
	(setq-default cursor-in-non-selected-windows nil)
	(mapc (lambda (elt)
		(with-current-buffer (car elt)
		  (setq-local mode-line-format nil)))
	  my-helm-bottom-buffers))

 (defun my-helm-bottom-buffers-show-mode-line ()
	(setq-default cursor-in-non-selected-windows t)
	(when my-helm-bottom-buffers
	  (mapc (lambda (elt)
		  (with-current-buffer (car elt)
		(setq-local mode-line-format (cdr elt))))
		my-helm-bottom-buffers)
	  (setq my-helm-bottom-buffers nil)))

 (defun my-helm-keyboard-quit-advice (orig-func &rest args)
	(my-helm-bottom-buffers-show-mode-line)
	(apply orig-func args))

 (add-hook 'helm-before-initialize-hook #'my-helm-bottom-buffers-init)
 (add-hook 'helm-after-initialize-hook #'my-helm-bottom-buffers-hide-mode-line)
 (add-hook 'helm-exit-minibuffer-hook #'my-helm-bottom-buffers-show-mode-line)
 (add-hook 'helm-cleanup-hook #'my-helm-bottom-buffers-show-mode-line)
 (advice-add 'helm-keyboard-quit :around #'my-helm-keyboard-quit-advice)

Hide Minibuffer in Helm

Hide minibuffer while helm is active

 (defun my-helm-hide-minibuffer-maybe ()
	(when (with-helm-buffer helm-echo-input-in-header-line)
	  (let ((ov (make-overlay (point-min) (point-max) nil nil t)))
	(overlay-put ov 'window (selected-window))
	(overlay-put ov 'face (let ((bg-color (face-background 'default nil)))
				`(:background ,bg-color :foreground ,bg-color)))
	(setq-local cursor-type nil))))
 (add-hook 'helm-minibuffer-set-up-hook #'helm-hide-minibuffer-maybe)

Helm packages

Helm ag
(use-package helm-ag  
  :load-path "~/.emacs.d/.local/elisp/emacs-helm-ag"
  :commands (helm-ag helm-ag-buffers helm-ag-this-file helm-do-ag helm-ag-project-root cpm/helm-files-do-ag cpm/helm-files-search-current-directory)
  :custom
  (helm-follow-mode-persistent t)
  :config
  (setq helm-ag-base-command "rg --no-heading")
  (setq helm-ag-fuzzy-match t))
Helm descbinds
(use-package helm-descbinds 
  :commands helm-descbinds
  :config
  (setq helm-descbinds-window-style 'same-window)
  (add-hook 'helm-mode-hook 'helm-descbinds-mode))
Helm git list
(use-package helm-ls-git :commands helm-ls-git-ls)
Helm hunks
(use-package helm-hunks :commands helm-hunks)
Helm swoop

Search on steroids

(use-package helm-swoop
  :commands (helm-swoop-without-pre-input helm-swoop-back-to-last-point helm-multi-swoop helm-multi-swoop-all)
  :load-path "~/.emacs.d/.local/elisp/helm-swoop/"
  :config
  (setq helm-swoop-use-fuzzy-match t)
  (setq helm-swoop-split-with-multiple-windows t))
Helm flyspell

Use helm with flyspell

(use-package helm-flyspell
  :disabled t
  ;; :if (not noninteractive)
  :commands helm-flyspell-correct
  :after flyspell
  :demand t
  :config
  (general-define-key :keymaps 'flyspell-mode-map
     "C-;" 'helm-flyspell-correct))
Helm recent directories

Recent directories

(use-package helm-dired-recent-dirs
    :commands helm-dired-recent-dirs-view)
Helm files
(use-package helm-files
  :ensure nil
  :defer t
  :config
  (setq helm-ff-skip-boring-files t)
  (setq helm-idle-delay 0.05)
  (setq helm-input-idle-delay 0.05)
  (setq helm-ff-file-name-history-use-recentf t)
  (setq helm-boring-file-regexp-list
  '("\\.git$" "\\.hg$" "\\.svn$" "\\.CVS$" "\\._darcs$" "\\.la$" "\\.o$" "~$"
    "\\.so$" "\\.a$" "\\.elc$" "\\.fas$" "\\.fasl$" "\\.pyc$" "\\.pyo$")))
Helm interface for themes (helm-themes)
(use-package helm-themes
  :commands helm-themes)
(defadvice helm-themes--load-theme (after helm-themes--load-theme-after activate) (require 'powerline) (powerline-reset)) 
;; (ad-unadvise 'helm-themes--load-theme)
Helm Projectile
(use-package helm-projectile
 :commands (helm-projectile-switch-to-buffer
            helm-projectile-find-dir
            helm-projectile-dired-find-dir
            helm-projectile-recentf
            helm-projectile-find-file
            helm-projectile-grep
            helm-projectile
            helm-projectile-switch-project)
 :init
 (setq projectile-switch-project-action 'helm-projectile)
 :config 
 (helm-projectile-on))

Ivy

Generic completion frontend that’s similar to helm but less enormous of a code base. Let’s install and enable it.

(use-package ivy 
  :diminish ivy-mode
  :general
  (:keymaps 'ivy-minibuffer-map
    "C-j" 'ivy-next-line
    "C-k" 'ivy-previous-line)
  :config
  (setq ivy-use-virtual-buffers t
        ;; number of result lines to display
        ivy-height 10
        ;; no regexp by default
        ivy-initial-inputs-alist nil
        ivy-re-builders-alist
        ;; allow input not in order
        '((t   . ivy--regex-ignore-order))
        ivy-count-format "%d/%d "))

Counsel

Counsel allows us to utilize ivy by replacing many built-in and common functions with richer versions.

(use-package counsel-projectile :commands counsel-projectile-bookmark)
(use-package counsel
  :commands (council-org-goto jump-in-buffer)
  :config
  (map! (:map counsel-mode-map
         :ni "C-j" #'ivy-next-line
         :ni "C-k" #'ivy-previous-line)))

Swiper

Swiper is an awesome searching utility with a quick preview. Let’s install it and load it when swiper or swiper-all is called.

(use-package swiper
  :commands (swiper swiper-all))

Imenu-List

(use-package imenu-list
  :ensure t
  :commands (imenu-list-smart-toggle imenu-list-minor-mode)
  :config
  (setq imenu-list-focus-after-activation t
        imenu-list-auto-resize t
        imenu-list-position 'left)
  :custom-face
  (imenu-list-entry-face-0 ((t (:inherit imenu-list-entry-face :foreground "#269bd2"))))
  (imenu-list-entry-face-1 ((t (:inherit imenu-list-entry-face :foreground "medium sea green"))))
  (imenu-list-entry-face-2 ((t (:inherit imenu-list-entry-face :foreground "#cb4b16"))))
  (imenu-list-entry-face-3 ((t (:inherit imenu-list-entry-face :foreground "#b58900")))))

Historian

Completion history

(use-package historian
  :defer 5
  :load-path "~/.emacs.d/.local/elisp/historian"
  :config
  (setq historian-save-file (concat cpm-cache-dir ".historian"))
  (historian-mode 1))

Saveplace

(use-package saveplace
  :init
  (save-place-mode 1)
  :config
  (setq save-place-file (concat cpm-cache-dir "saved-places")
  ;; (setq save-place-forget-unreadable-files nil)
))

Ace Window

Ace window management.

(use-package ace-window
  :commands (ace-window ace-swap-window aw-flip-window cpm/swap-windows))

Avy

(use-package avy
  :commands (avy-goto-char))

Windows

Golden Ratio

Automatic resizing of Emacs windows to the golden ratio

(use-package golden-ratio
  :load-path "~/.emacs.d/.local/elisp/golden-ratio.el/"
  :ensure nil
  ;; :after (:any perspective helm nameframe projectile) 
  ;; :demand t
  :defer 3
  :config
  (setq golden-ratio-exclude-buffer-names '("*Ilist*"))
  (setq golden-ratio-exclude-buffer-regexp '("Ilist"))
  ;; inhibit in helm windows
  (defun cpm--helm-alive-p ()
  (if (boundp 'helm-alive-p)
      (symbol-value 'helm-alive-p)))
      (add-to-list 'golden-ratio-inhibit-functions 'cpm--helm-alive-p)
  ;;fix for ispell
  (defun cpm--ispell-alive-p ()
    (get-buffer ispell-choices-buffer))
  (add-to-list 'golden-ratio-inhibit-functions 'cpm--ispell-alive-p)
  ;; use golden ratio for the following    
  (setq golden-ratio-extra-commands
        (append golden-ratio-extra-commands
                '(evil-window-left
                  evil-window-right
                  evil-window-up
                  evil-window-down
                  buf-move-left
                  buf-move-right
                  buf-move-up
                  buf-move-down
                  window-number-select
                  select-window
                  select-window-1
                  select-window-2
                  select-window-3
                  select-window-4
                  select-window-5
                  select-window-6
                  select-window-7
                  select-window-8
                  select-window-9
                  previous-multiframe-window
                  magit-status)))
  (golden-ratio-mode 1))

Window Numbering

Numbered window shortcuts for Emacs

 (use-package window-numbering
   :defer 1
   :config
   (defun window-numbering-install-mode-line (&optional position)
   "Do nothing, the display is handled by the powerline.")
   (setq window-numbering-auto-assign-0-to-minibuffer nil)
   
   (window-numbering-mode 1)

;; make sure neotree is always 0
 (defun spacemacs//window-numbering-assign ()
   "Custom number assignment for neotree."
   (when (and (boundp 'neo-buffer-name)
              (string= (buffer-name) neo-buffer-name)
              ;; in case there are two neotree windows. Example: when
              ;; invoking a transient state from neotree window, the new
              ;; window will show neotree briefly before displaying the TS,
              ;; causing an error message. the error is eliminated by
              ;; assigning 0 only to the top-left window
              (eq (selected-window) (window-at 0 0)))
     0))

 ;; using lambda to work-around a bug in window-numbering, see
 ;; https://github.com/nschum/window-numbering.el/issues/10
 (setq window-numbering-assign-func
       (lambda () (spacemacs//window-numbering-assign))))

Unset window keys

A nice tip from Pragmatic emacs

;; unset C- and M- digit keys
(dotimes (n 10)
  (global-unset-key (kbd (format "C-%d" n)))
  (global-unset-key (kbd (format "M-%d" n)))
  )

Windmove

(use-package windmove
  :commands (windmove-up windmove-down windmove-left windmove-right)
  :config
  (defun cpm/split-window-right-and-focus ()
  "Split the window horizontally and focus the new window."
  (interactive)
  (split-window-right)
  (windmove-right))
  (defun cpm/split-window-below-and-focus ()
  "Split the window vertically and focus the new window."
  (interactive)
  (split-window-below)
  (windmove-down))
  ;; add edit mode keybindings
  (global-set-key (kbd "<H-up>")     'windmove-up)
  (global-set-key (kbd "<H-down>")   'windmove-down)
  (global-set-key (kbd "<H-left>")   'windmove-left)
  (global-set-key (kbd "<H-right>")  'windmove-right)
  )

Winner

Winner mode is a built-in package for restoring window configurations

(use-package winner
 :ensure nil
 :commands (winner-undo winner-redo winner-mode)
 :config
 (winner-mode 1))

Other window

Move to other window

(general-define-key :states '(normal motion visual insert)
  "C-o" 'other-window)

Recent files

(use-package recentf
  :commands (helm-recentf)
  :config 
  (setq recentf-save-file (concat cpm-etc-dir "recentf"))
  ;; remove agenda files from list.
  (setq recentf-exclude '("projects.org"
                          "inbox.org"
                          "someday.org"
                          "bookmark")
        recentf-max-saved-items 300
        recentf-max-menu-items 10))

Treemacs

Treemacs is a file and project explorer

(use-package treemacs
  :ensure t
  :commands treemacs
  :init
  (with-eval-after-load 'winum
    (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
  :config
  (progn
    (setq treemacs-collapse-dirs              (if (executable-find "python") 3 0)
          treemacs-file-event-delay           5000
          treemacs-follow-after-init          t
          treemacs-follow-recenter-distance   0.1
          treemacs-goto-tag-strategy          'refetch-index
          treemacs-indentation                2
          treemacs-indentation-string         " "
          treemacs-is-never-other-window      nil
          treemacs-no-png-images              nil
          treemacs-project-follow-cleanup     nil
          treemacs-persist-file               (concat cpm-cache-dir "treemacs-persist") 
          treemacs-recenter-after-file-follow nil
          treemacs-recenter-after-tag-follow  nil
          treemacs-show-hidden-files          t
          treemacs-silent-filewatch           nil
          treemacs-silent-refresh             nil
          treemacs-sorting                    'alphabetic-desc
          treemacs-space-between-root-nodes   t
          treemacs-tag-follow-cleanup         t
          treemacs-tag-follow-delay           1.5
          treemacs-width                      35)

    (treemacs-follow-mode t)
    (treemacs-filewatch-mode t)
    (pcase (cons (not (null (executable-find "git")))
                 (not (null (executable-find "python3"))))
      (`(t . t)
       (treemacs-git-mode 'extended))
      (`(t . _)
       (treemacs-git-mode 'simple)))))
  ;; :bind
  ;; (:map global-map
  ;;       ("M-0"       . treemacs-select-window)
  ;;       ("C-x t 1"   . treemacs-delete-other-windows)
  ;;       ("C-x t t"   . treemacs)
  ;;       ("C-x t B"   . treemacs-bookmark)
  ;;       ("C-x t C-t" . treemacs-find-file)
  ;;       ("C-x t M-t" . treemacs-find-tag)))

(use-package treemacs-evil
  :after treemacs evil
  :ensure t)

(use-package treemacs-projectile
  :after treemacs projectile
  :ensure t)

Other UI

Centered Cursor Mode

Keep the cursor centered in the screen

(use-package centered-cursor-mode
  :diminish centered-cursor-mode
  :hook ((markdown-mode org-mode) . centered-cursor-mode)
  :commands (centered-cursor-mode
             global-centered-cursor-mode)
  :config
  (progn
    (setq ccm-recenter-at-end-of-file t
          ccm-ignored-commands '(mouse-drag-region
                                 mouse-set-point
                                 widget-button-click
                                 scroll-bar-toolkit-scroll
                                 evil-mouse-drag-region))))

Namespaced Keybindings

I use a lot of keybindings, with <SPC> as my “leader” key.

Application Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"
 
  "a"  '(:ignore t :which-key "Applications") 
  "ac" '(:ignore t :which-key "Cmus")
  "ad" 'dired-jump
  "ae" 'eshell
  "am" 'multi-term
  "ar" 'ranger
  "as" 'sane-term
  "aw" 'wttrin
  )
Buffer Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

  "b"  '(:ignore t :which-key "Buffers")
  "bb" 'helm-mini
  "bc" 'cpm/copy-whole-buffer-to-clipboard
  "bD" 'kill-buffer-and-window
  "bd" 'kill-this-buffer
  "be" 'erase-buffer
  "bf" 'cpm/browse-file-directory
  "bj" 'cpm/jump-in-buffer
  "bk" 'evil-delete-buffer
  "bK" 'crux-kill-other-buffers
  "bn" 'evil-buffer-new
  "bN" 'cpm/new-buffer-new-frame
  "br" 'revert-buffer
  "bR" 'crux-rename-buffer-and-file
  "bt" 'open-dir-in-iterm
  )
Comment Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

  "c"  '(:ignore t :which-key "Commenting")
  "cb" 'org-block-wrap
  "cc" 'evil-commentary
  "cl" 'evil-commentary-line
  "cy" 'evil-commentary-yank-line
 )
Config Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

  "C"  '(:ignore t :which-key "Config")
  "Cc" 'goto-config.org
  "Cd" 'goto-dotfiles.org
  "CD" 'goto-emacs-dir
  "Ck" 'cpm/compile-dotemacs
  "CK" 'cpm/delete-byte-compiled-files
  "Cl" 'load-config
  "Ci" 'goto-init.el
  "Co" 'goto-org-files
  "Cs" 'goto-custom.el
  )
File Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

    "f"  '(:ignore t :which-key "Files")
    "fd" 'fzf-directory
    "ff" 'helm-find-files
    "fl" 'helm-locate
    "fo" 'crux-open-with
    "fs" 'save-buffer
    "fr" 'helm-recentf
    "fy" 'spacemacs/show-and-copy-buffer-filename
    "fz" 'fzf
    )
General Keybindings
(general-define-key
 :states '(normal motion visual insert emacs)
 :keymaps 'override
 :prefix "SPC"
 :non-normal-prefix "C-SPC"

   "A" 'helm-apropos
   "B" #'cpm/dashboard
   "?" 'helm-descbinds
   "<SPC>" 'helm-M-x
   ;; "d" #'deer
   "d" #'dired-jump
   "D" #'dired-jump-other-window
   ;; "D" #'cpm/deer-split-window
   "E" 'cpm/call-emacs
   "e" 'server-edit
   "G" 'general-describe-keybindings
   "j" 'avy-goto-char
   "k" 'helm-show-kill-ring
   "l" 'cpm/last-search-buffer
   ;; "N" 'research-notes
   "n" 'big-notes
   "r" 'cpm/resume-last-jump
   "S" 'hydra-spelling/body
   ;; "W" 'woman
   "`" 'beacon-blink
   "'" 'shell-pop
   "." 'quick-commit
   ";" 'evil-commentary-line
   "[" 'cpm/previous-user-buffer
   "]" 'cpm/next-user-buffer
   "TAB" 'switch-to-previous-buffer
   )
Make/Compile Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

  "M"   '(:ignore t :which-key "Make/Compile")
  "Mm"  'compile
  "Me"  'compile-goto-error
  "Mk"  'kill-compilation
  "Mr"  'recompile
  "Mv"  'cpm/make-move
)
Markdown Keybindings
(general-define-key
  :states '(normal motion)
  :keymaps 'markdown-mode-map
  :prefix "SPC m" 
  :non-normal-prefix "C-SPC m"

  ""    '(nil :which-key "Local Leader")
  "c"  '(:ignore t :which-key "command")
  "h"  '(:ignore t :which-key "insert")
  "i"  '(:ignore t :which-key "lists")
  "x"  '(:ignore t :which-key "text")

  ;; Movement
  "{"   'markdown-backward-paragraph
  "}"   'markdown-forward-paragraph

  ;; Completion, and Cycling
  "]"   'markdown-complete

  ;; Indentation
  ">"   'markdown-indent-region
  "<"   'markdown-exdent-region

  ;; Buffer-wide commands
  "c]"  'markdown-complete-buffer
  "cc"  'markdown-check-refs
  "ce"  'markdown-export
  "cm"  'markdown-other-window
  "cn"  'markdown-cleanup-list-numbers
  "co"  'markdown-open
  "cp"  'markdown-preview
  "cv"  'markdown-export-and-preview
  "cw"  'markdown-kill-ring-save

  ;; headings
  "hi"  'markdown-insert-header-dwim
  "hI"  'markdown-insert-header-setext-dwim
  "h1"  'markdown-insert-header-atx-1
  "h2"  'markdown-insert-header-atx-2
  "h3"  'markdown-insert-header-atx-3
  "h4"  'markdown-insert-header-atx-4
  "h5"  'markdown-insert-header-atx-5
  "h6"  'markdown-insert-header-atx-6
  "h!"  'markdown-insert-header-setext-1
  "h@"  'markdown-insert-header-setext-2

  ;; Insertion of common elements
  "-"   'markdown-insert-hr
  "if"  'markdown-insert-footnote
  "ii"  'markdown-insert-image
  "ik"  'spacemacs/insert-keybinding-markdown
  "iI"  'markdown-insert-reference-image
  "il"  'markdown-insert-link
  "iL"  'markdown-insert-reference-link-dwim
  "iw"  'markdown-insert-wiki-link
  "iu"  'markdown-insert-uri

  ;; Element removal
  "k"   'markdown-kill-thing-at-point

  ;; Numbering
  "n"   #'markdown-cleanup-list-numbers
  ;; List editing
  "li"  'markdown-insert-list-item

  ;; region manipulation
  "xb"  'markdown-insert-bold
  "xi"  'markdown-insert-italic
  "xc"  'markdown-insert-code
  "xC"  'markdown-insert-gfm-code-block
  "xq"  'markdown-insert-blockquote
  "xQ"  'markdown-blockquote-region
  "xp"  'markdown-insert-pre
  "xP"  'markdown-pre-region

  ;; Following and Jumping
  "N"   'markdown-next-link
  "f"   'markdown-follow-thing-at-point
  "P"   'markdown-previous-link
  "<RET>" 'markdown-do

  "gj"    #'markdown-next-visible-heading
  "gk"    #'markdown-previous-visible-heading
  ;; Assumes you have a markdown renderer plugin in chrome
  "M-r"   #'browse-url-of-file
  "h]"    #'markdown-next-visible-heading
  "h["    #'markdown-previous-visible-heading
  "p["    #'markdown-promote
  "p]"    #'markdown-demote
  "l["    #'markdown-next-link
  "l]"    #'markdown-previous-link
 ) 

(general-define-key
  :states '(normal motion insert)
  :keymaps 'markdown-mode-map

  "s-*"      #'markdown-insert-list-item
  "s-b"      #'markdown-insert-bold
  "s-i"      #'markdown-insert-italic

  "M--"      #'markdown-insert-hr
  "TAB"      #'markdown-cycle
  "M-h"      #'markdown-promote
  "M-j"      #'markdown-move-down
  "M-k"      #'markdown-move-up
  "M-l"      #'markdown-demote

  "M-<up>"   #'markdown-move-list-item-up
  "M-<down>" #'markdown-move-list-item-down

  )
Miscellaneous Keybindings
;; Show which-key top-level bindings
(global-set-key (kbd "H-k") 'which-key-show-top-level)
;; override evil insert for kill line
(general-define-key :states '(insert) "C-k" 'kill-line)
Package Keybindings

Keybindings for managing packages

(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

    "P" '(:ignore t :which-key "Packages")
    "Pl" 'paradox-list-packages
    "Pu" 'paradox-upgrade-packages
    "Pc" 'finder-commentary
    )
(global-set-key (kbd "C-h C-c") 'finder-commentary)
Project Keybindings
(general-define-key
  :states '(normal visual emacs motion)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

    "p" '(:ignore t :which-key "Projects")
    "p!"  'projectile-run-shell-command-in-root
    "p&"  'projectile-run-async-shell-command-in-root
    "pa"  'projectile-toggle-between-implementation-and-test
    "pb"  'helm-projectile-switch-to-buffer
    "pc"  'projectile-compile-project
    "pC"  'desktop+-create
    "pd"  'helm-projectile-find-dir
    "pD"  'cpm/hydra-desktop
    ;; "pD"  'projectile-dired
    "pf"  'helm-projectile-find-file
    "pF"  #'cpm/helm-projectile-find-file-other-window
    "pg"  'cpm/goto-projects
    "ph"  'helm-projectile
    "pJ"  'bmkp-desktop-jump
    "pG"  'projectile-regenerate-tags
    "pI"  'projectile-invalidate-cache
    "pk"  'projectile-kill-buffers
    ;; "pl"  'desktop+-load
    "po"  'projectile-multi-occur
    "pp"  'helm-projectile-switch-project
    "pP"  'projectile-persp-switch-project
    ;; "pp"  'helm-persp-projectile-switch-project
    "pr"  'helm-projectile-recentf
    "pR"  'projectile-replace
    "pS"  'persp-switch
    "ps"  '(:ignore t :which-key "Frames")
    "pss"  'nameframe-switch-frame
    "ps1" #'cpm/load-phil101
    "ps2" #'cpm/load-phil232
    "ps5" #'cpm/load-phil105
    "ps8" #'cpm/load-phil871
    "psa" #'cpm/load-kant-apperception-substance
    "psb" #'cpm/load-kant-agency-book
    "psc" #'cpm/load-emacs-config
    "psf" #'cpm/load-kant-free-thought
    "psr" #'cpm/load-kant-reflection
    "pst" #'cpm/load-org-agenda-todo
    "psw" #'cpm/load-website
    "pt"  #'cpm/search-todo-markers
    "pT"  'projectile-find-test-file
    "pv"  'hydra-persp/body
    "pV"  'projectile-vc
    "py"  'projectile-find-tag
  )
Quit Keybindings
(general-define-key
 :states '(normal motion visual insert emacs)
 :keymaps 'override
 :prefix "SPC"
 :non-normal-prefix "C-SPC"

   "q"  '(:ignore t :which-key "Quit")
   "qq" 'cpm/save-desktop-save-buffers-kill-emacs
   "qQ" 'evil-quit-all
   "qr" 'restart-emacs
   )
Search Keybindings
(general-define-key
 :states '(normal motion visual insert emacs)
 :keymaps 'override
 :prefix "SPC"
 :non-normal-prefix "C-SPC"

   "s" '(:ignore t :which-key "Search")
   "sa" 'helm-org-rifle-agenda-files
   "sd" 'cpm/helm-files-search-current-directory ; search current buffer's directory
   "sD" 'cpm/helm-files-do-ag ; search with directory input
   "sb" 'helm-ag-buffers
   "sf" 'helm-do-ag-this-file
   "sj" 'cpm/forward-or-backward-sexp
   "sk" 'helm-show-kill-ring
   "sl" 'last-search-buffer
   "so" 'helm-occur
   "sp" 'helm-ag-project-root
   "sr" #'vr/query-replace
   "sR" 'helm-org-rifle
   "ss" 'helm-swoop-without-pre-input ;; search with swoop in file
   "sS" #'cpm/flyspell-ispell-goto-next-error ;; search for next spelling error
   "st" #'cpm/search-file-todo-markers ;; search for TODOs in file w/helm-ag
   "sT" #'cpm/search-todo-markers ;; search todo markers in directory w/helm-ag
   "/"  'cpm/helm-files-search-current-directory   ;; search in directory with word prompt
    )
Toggle Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

    "t"  '(:ignore t :which-key "Toggles")
    "ta" 'company-mode
    "tb" 'buffer-line-mode
    "tB" 'beacon-mode
    "tc" 'centered-cursor-mode
    "tC" 'centered-window-mode
    "td" 'cpm/osx-toggle-menubar-theme
    "tf" 'toggle-serif
    "tF" 'toggle-frame-maximized
    "tg" 'git-gutter-mode
    "tG" 'golden-ratio-mode 
    "th" 'hl-line-mode
    "te" 'toggle-indicate-empty-lines
    "tE" 'eldoc-mode
    "tl" #'imenu-list-smart-toggle
    "tm" #'treemacs
    "tM" 'hidden-mode-line-mode
    ;; "tn" 'nlinum-mode
    "tn" 'display-line-numbers-mode
    "tN" 'org-numbers-overlay-mode
    "to" 'org-toggle-link-display
    "tO" 'outline-toc-mode
    "tp" 'smartparens-mode
    "tP" 'show-paren-mode 
    "tr" 'rainbow-identifiers-mode
    "ts" 'flyspell-mode
    "tS" 'ispell-buffer
    "tt" 'toggle-dark-light-theme
    "tT" 'helm-themes
    "tw" 'writeroom-mode
    "tz" 'zone
    ;; "tt" 'counsel-load-theme
  )
User Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

    "u"  '(:ignore t :which-key "User")
    "uA" '(:ignore t which-key "Agenda Files")
    "uAa" #'cpm/goto-articles.org
    "uAc" #'cpm/goto-classes.org
    "uAf" #'cpm/goto-org-files
    "uAi" #'cpm/goto-inbox.org
    "uAn" #'cpm/goto-notes.org
    "uAp" #'cpm/goto-projects.org
    "uAs" #'cpm/goto-someday.org
    "uAt" #'cpm/org-goto-todo
    "ua"  '(:ignore t :which-key "Agenda")
    "uaa" 'cpm/jump-to-org-super-agenda
    "uaw" 'cpm/jump-to-week-agenda
    "um" 'cpm/org-to-markdown
    "uc" 'cpm/pandoc-convert-to-pdf
    "uC" 'cpm/pandoc-command-line-convert-to-pdf
    "ug" 'org-mac-grab-link
    "ui" 'cpm/org-goto-inbox
    "uk" 'kill-compilation
    "ul" 'desktop-read
    "uo" 'cpm/markdown-to-org
    "up" 'run-pandoc
    "uP" 'cpm/pandoc-pdf-open
    "ur" 'remember-notes
    "us" 'sb-expand-current-file
    "uS" 'just-one-space
    ;; "ut" 'cpm/org-goto-todo
    "ut" 'cpm/jump-to-org-agenda-all-todos
    "ud" 'distraction-free
    "uD" 'my-desktop
    "uj" 'cpm/goto-journal
    ;; "op" 'pandoc-convert-to-pdf
    "uw" 'count-words
    "uW" 'osx-dictionary-search-input
    "ux" 'helm-bibtex
    )
Version Control Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

  "g"  '(:ignore t :which-key "Git")
  "gb" 'magit-blame
  "gc" 'magit-commit
  "gd" 'magit-diff
  "gl" 'magit-log
  "gn" 'git-gutter:next-hunk
  "gp" 'git-gutter:previous-hunk
  "gr" 'magit-reflog
  "gs" 'magit-status
  )
Window Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

  "0" 'select-window-0
  "1" 'select-window-1
  "2" 'select-window-2
  "3" 'select-window-3
  "4" 'select-window-4
  "5" 'select-window-5

  "w"  '(:ignore t :which-key "Windows")
  "wa" 'ace-window
  "wf" 'cpm/toggle-window-split
  "wc" 'delete-window
  "wd" 'delete-window
  "wm" 'delete-other-windows
  "wr" 'cpm/rotate-windows
  "wR" 'cpm/rotate-windows-backward
  "wu" 'winner-undo
  "wU" 'winner-redo
  "wv" 'cpm/split-window-right-and-focus
  "wV" 'evil-window-vsplit
  "wx" 'cpm/window-exchange
  "w-" 'evil-window-split
  "w_" 'cpm/split-window-below-and-focus
  )
Wiki Keybindings
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"

    "W" '(:ignore t :which-key "Wiki")
    ;; Keys in visualize mode
    "Wp" 'org-brain-add-parent
    "WP" 'org-brain-remove-parent
    "Wc" 'org-brain-add-child
    "WC" 'org-brain-remove-child
    "Wh" 'org-brain-new-child
    "Wn" 'org-brain-pin
    "Wt" 'org-brain-set-title
    "Wj" 'forward-button
    "Wk" 'backward-button
    "Wo" 'org-brain-goto-current
    "WO" 'org-brain-goto
    "Wv" 'org-brain-visualize
    "Wf" 'org-brain-add-friendship
    "WF" 'org-brain-remove-friendship
    "Wd" 'org-brain-delete-entry
    "Wl" 'org-brain-add-resource
    "Wa" 'org-brain-visualize-attach
    "WA" 'org-brain-archive
    "Wb" 'org-brain-visualize-back
    "W\C-y" 'org-brain-visualize-paste-resource
    "WT" 'org-brain-set-tags
    "Wq" 'org-brain-visualize-quit
    "Wr" 'org-brain-visualize-random
    "WR" 'org-brain-visualize-wander
    "Wm" 'org-brain-visualize-mind-map
    "W+" 'org-brain-visualize-add-grandchild
    "W-" 'org-brain-visualize-remove-grandchild
    "Wz" 'org-brain-visualize-add-grandparent
    "WZ" 'org-brain-visualize-remove-grandparent)


  ;;   "Wc" 'org-wiki-close
  ;;   "Wd" 'org-wiki-dired-all
  ;;   "Wk" 'org-wiki-close
  ;;   "Wh" 'org-wiki-helm
  ;;   "WH" 'org-wiki-help
  ;;   "WI" 'org-wiki-index
  ;;   "Wi" 'org-wiki-insert
  ;;   "Wl" 'org-wiki-link
  ;;   "Wm" 'org-wiki-make-page
  ;;   "Wv" 'org-wiki-server-toggle
  ;;   "We" 'org-wiki-export-html
  ;;   "Wp" 'org-wiki-panel
  ;;   "Ws" 'org-wiki-search
  ;;   "Wt" 'org-wiki-header
  ;; )
Zettelkasten Notes
(general-define-key
  :states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"
 
  "z"  '(:ignore t :which-key "Zettelkasten")
  "zc" 'zd-search-current-id
  "zf" 'zd-avy-file-search
  "zi" 'zd-find-file-id-insert
  "zI" 'zd-find-file-full-title-insert
  "zl" 'zd-avy-link-search
  "zL" 'zd-insert-list-links
  "zn" 'zd-new-file
  "zN" 'zd-new-file-and-link
  "zo" 'zd-find-file
  "zO" 'zd-org-include-search
  "zr" 'deft-refresh
  "zs" 'zd-deft-new-search
  "zS" 'zd-search-at-point
  "zt" 'zd-avy-tag-search
  "zT" 'zd-insert-org-title
 ) 

Smooth Scrolling

;; Keyboard smooth scrolling: Prevent the awkward "snap to re-center" when
 ;; the text cursor moves off-screen. Instead, only scroll the minimum amount
 ;; necessary to show the new line. (A number of 101+ disables re-centering.)
 (setq scroll-conservatively 101)

 ;; Optimize mouse wheel scrolling for smooth-scrolling trackpad use.
 ;; Trackpads send a lot more scroll events than regular mouse wheels,
 ;; so the scroll amount and acceleration must be tuned to smooth it out.
 (setq
  ;; If the frame contains multiple windows, scroll the one under the cursor
  ;; instead of the one that currently has keyboard focus.
  mouse-wheel-follow-mouse 't
  ;; Completely disable mouse wheel acceleration to avoid speeding away.
  mouse-wheel-progressive-speed nil
  ;; The most important setting of all! Make each scroll-event move 2 lines at
  ;; a time (instead of 5 at default). Simply hold down shift to move twice as
  ;; fast, or hold down control to move 3x as fast. Perfect for trackpads.
  mouse-wheel-scroll-amount '(2 ((shift) . 4) ((control) . 6)))

Which-key

(use-package which-key
  :defer 1
  :diminish ""
  :config
  (setq which-key-special-keys nil)
  ;; Set the time delay (in seconds) for the which-key popup to appear.
  (setq which-key-idle-delay .3)
  (which-key-mode))

Doom-Quit

Quit prompt with messages from Doom exit messages

(defun doom-quit-p (&optional prompt)
  "Return t if this session should be killed. Prompts the user for
confirmation."
(or (yes-or-no-p (format "››› %s" (or prompt "Quit Emacs?")))
    (ignore (message "Aborted"))))
(setq confirm-kill-emacs nil)
(add-hook 'kill-emacs-query-functions #'doom-quit-p)
(defvar +doom-quit-messages
  '(;; from Doom 1
    "Let's beat it -- This is turning into a bloodbath!"
    "I wouldn't leave if I were you. DOS is much worse."
    "Ya know, next time you come in here I'm gonna toast ya."
    "Go ahead and leave. See if I care."
    "Are you sure you want to quit this great editor?"
    ;; Custom
    "Emacs! Emacs!! Emacs!!!"
    "The King is dead, long live the King!"
    "Like you have somewhere better to be..."
    "Don't worry, I won't tell everyone you're a failure"
    "Aus so krummem Holze, als woraus der Mensch gemacht ist, kann nichts ganz Gerades gezimmert werden"
    "(setq nothing t everything 'permitted)"
    "Emacs will remember that."
    "Emacs, Emacs never changes."
    "Hey! Hey, M-x listen!"
    "Okay, look. We've both said a lot of things you're going to regret..."
    "You are *not* prepared!")
  "A list of quit messages, picked randomly by `+doom-quit'. Taken from
http://doom.wikia.com/wiki/Quit_messages and elsewhere.")

(defun +doom|quit (&rest _)
  (doom-quit-p
   (format "%s  Quit?"
           (nth (random (length +doom-quit-messages))
                +doom-quit-messages))))

(remove-hook 'kill-emacs-query-functions #'doom-quit-p)
(add-hook 'kill-emacs-query-functions #'+doom|quit)

Popup Windows

A package that puts an end to popped-up windows not behaving they way you’d like them to.

(use-package shackle
  :after helm
  :config
  ;; make helm pop-ups behave
  (setq helm-display-function #'pop-to-buffer)
  (setq shackle-rules '(("\\`\\*helm.*?\\*\\'" :regexp t :align t :ratio 0.46)))
  (shackle-mode 1))

Helpful (Documentation)

Contextual help for emacs

(use-package helpful
  :config (evil-set-initial-state 'helpful-mode 'motion)
  :general
  ("C-h f" #'helpful-callable)
  ("C-h k" #'helpful-key)
  ("C-h v" #'helpful-variable)
  ("C-c C-." #'helpful-at-point)
  ("C-h C-l" #'find-library)
  :commands (helpful-function helpful-callable helpful-key helpful-variable helpful-at-point))

Clickable Links

Courtesy of Álvaro Ramírez

(use-package goto-addr
  :hook ((compilation-mode . goto-address-mode)
         (prog-mode . goto-address-prog-mode)
         (eshell-mode . goto-address-mode)
         (shell-mode . goto-address-mode))
  :bind (:map goto-address-highlight-keymap
              ("<RET>" . goto-address-at-point)
              ("M-<RET>" . newline))
  :commands (goto-address-prog-mode
             goto-address-mode))

Programming

Alignment

This package provides gl and gL align operators: gl MOTION CHAR and right-align gL MOTION CHAR

(use-package evil-lion
  :defer t
  :config
  (general-define-key :states '(normal) :keymaps 'prog-mode-map
  "g l" 'evil-lion-left
  "g L" 'evil-lion-right)

  (general-define-key :states '(visual) :keymaps 'prog-mode-map
  "g l" 'evil-lion-left
  "g L" 'evil-lion-right)
  )

Rainbow delimiters

Useful package that will highlight delimiters such as parentheses, brackets or braces according to their depth. Each successive level is highlighted in a different color. This makes it easy to spot matching delimiters, orient yourself in the code, and tell which statements are at a given depth.

(use-package rainbow-delimiters 
  :commands rainbow-delimiters-mode
  :init
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
  :config
  (set-face-attribute 'rainbow-delimiters-unmatched-face nil
         :foreground "red"
         :inherit 'error
         :box t)) 

Rainbow identifiers

Rainbow identifiers mode is an Emacs minor mode providing highlighting of identifiers based on their names. Each identifier gets a color based on a hash of its name.

(use-package rainbow-identifiers
  :commands rainbow-identifiers-mode
  :init
  (add-hook 'prog-mode-hook 'rainbow-identifiers-mode))

Rainbow mode

Colorize color names in buffers

(use-package rainbow-mode
  :commands rainbow-mode)

Electric Pair (Autopair)

(use-package electric-pair
  :disabled t
  :ensure nil
  :commands electric-pair-mode
  :init
  (add-hook 'prog-mode-hook 'electric-pair-mode)
  (add-hook 'org-mode-hook 'electric-pair-mode)
  (add-hook 'markdown-mode-hook 'electric-pair-mode)
  :config 
  ;; via https://www.topbug.net/blog/2016/09/29/emacs-disable-certain-pairs-for-electric-pair-mode/
  (setq electric-pair-inhibit-predicate
      (lambda (c)
        (if (char-equal c ?\") t (electric-pair-default-inhibit c)))))

Languages

Applescript

(use-package applescript-mode
  :ensure t
  :mode (("\\.scpt\\'" . applescript-mode))
  :commands (applescript-mode))

Elisp

(use-package elisp-slime-nav
  :commands elisp-slime-nav-mode
  :hook ((emacs-lisp-mode ielm-mode) . (elisp-slime-nav-mode)))

(use-package eldoc
  :commands eldoc-mode
  :diminish eldoc-mode
  :config 
  ;; Show ElDoc messages in the echo area immediately, instead of after 1/2 a second.
  (setq eldoc-idle-delay 0))
  ;; Elisp hook
  (add-hook 'emacs-lisp-mode-hook (lambda ()
              (setq show-trailing-whitespace t)
              (prettify-symbols-mode)
              (eldoc-mode)
              (yas-minor-mode)
              (company-mode)
              (rainbow-delimiters-mode)))

Haskell

(use-package haskell-mode
  :commands haskell-mode)

Html

(use-package web-mode
  :commands (web-mode)
  :mode ("\\.html$" . web-mode)
  :config
  (setq web-mode-enable-auto-pairing t
        web-mode-enable-auto-expanding t
        web-mode-enable-css-colorization t
        web-mode-enable-auto-closing t
        web-mode-enable-auto-quoting t)) 

Lua

(use-package lua-mode
  :commands lua-mode
  :init
  (dolist (pattern '("\\.lua\\'"))
  (add-to-list 'auto-mode-alist (cons pattern 'lua-mode))))

PHP

(use-package php-mode
  :commands php-mode
  :init
  (dolist (pattern '("\\.php\\'"))
  (add-to-list 'auto-mode-alist (cons pattern 'php-mode))))

Shell script mode

(use-package sh-script
  :commands sh-script-mode
  :init
  (progn
    ;; Use sh-mode when opening `.zsh' files, and when opening Prezto runcoms.
    (dolist (pattern '("\\.zsh\\'"
                       "zlogin\\'"
                       "zlogout\\'"
                       "zpreztorc\\'"
                       "zprofile\\'"
                       "zshenv\\'"
                       "zshrc\\'"))
      (add-to-list 'auto-mode-alist (cons pattern 'sh-mode)))))

  (defun spacemacs//setup-shell ()
      (when (and buffer-file-name
                 (string-match-p "\\.zsh\\'" buffer-file-name))
        (sh-set-shell "zsh")))
    (add-hook 'sh-mode-hook 'spacemacs//setup-shell)

Yaml

(use-package yaml-mode
  :commands yaml-mode
  :config
  (add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode))
  (add-to-list 'auto-mode-alist '("\\.yaml$" . yaml-mode))
  (add-hook 'yaml-mode-hook (lambda () (run-hooks 'prog-mode-hook)))
)

Vim

(use-package vimrc-mode
  :commands vimrc-mode)

Macrostep

Interactive macro expander for emacs

(use-package macrostep
  :commands macrostep-expand
) 

Documentation

(use-package tldr 
  :commands (tldr tldr-update-docs)
  :init 
  (evil-set-initial-state 'tldr-mode 'emacs)
  :config
  (setq tldr-directory-path (expand-file-name "tldr/" cpm-etc-dir)))

Session & Project Management

Hydra

(use-package hydra
  ;; :commands (cpm/hydra-desktop/body))
  :defer 2)
  ;; hydra for TODOs
  (with-eval-after-load 'hydra
  (defhydra cpm/hydra-todo 
             (:pre
              (hl-todo-mode 1)
              :post
              (hl-todo-mode -1))
  "Todo"
  ("n" hl-todo-next "Next")
  ("p" hl-todo-previous "Previous")
  ("o" hl-todo-occur "Occur")
  ("q" nil "Quit" :color blue :exit t)))

Projectile

(use-package projectile
 :defer 1
 :init
 ;; save projectile-known-projects-file in cache folder
  (setq projectile-known-projects-file
     (concat cpm-cache-dir "projectile-bookmarks.eld"))
  (setq projectile-cache-file
     (concat cpm-cache-dir "projectile.cache"))
  (setq projectile-enable-caching t
        projectile-files-cache-expire 60)
  :config
  (projectile-mode t))

Perspectives

Yet another attempt to manage buffers/workspaces

(use-package perspective
  :commands (persp-switch persp-add-buffer persp-set-buffer))

(use-package persp-projectile
    :commands projectile-persp-switch-project)
(with-eval-after-load 'persp-projectile
    (defhydra hydra-persp (:columns 4
                           :color blue)
      "Perspective"
      ("a" persp-add-buffer "Add Buffer")
      ("i" persp-import "Import")
      ("c" persp-kill "Close")
      ("n" persp-next "Next")
      ("p" persp-prev "Prev")
      ("k" persp-remove-buffer "Kill Buffer")
      ("r" persp-rename "Rename")
      ("A" persp-set-buffer "Set Buffer")
      ("s" persp-switch "Switch")
      ("C-x" persp-switch-last "Switch Last")
      ("b" persp-switch-to-buffer "Switch to Buffer")
      ("P" projectile-persp-switch-project "Switch Project")
      ("q" nil "Quit")))

(with-eval-after-load 'desktop+
(defhydra cpm/hydra-desktop (:columns 4
                             :color blue)
  "Desktops"
  ("c" desktop+-create "Create desktop")
  ("l" desktop+-load "Load desktop"))) 

Desktop Perspective Integration

A useful set of functions for loading perspectives via desktop-save

(defun perspectives-buffer-name-p (buffer)
    (if (and buffer
         (buffer-name buffer)
         (not (string-prefix-p "*" (buffer-name buffer)))
         (not (string-suffix-p "*" (buffer-name buffer))))
    t
      nil))

  (defun perspectives-hash-filter (current filtered parameters saving)
    (let ((value (cdr current))
      (result ())
      (keys (hash-table-keys (cdr current))))
      ;; for every perspective...
      (dolist (key keys)
    (let ((persp (gethash key value)))
      ;; that isn't killed...
      (if (not (persp-killed persp))
          (add-to-list
           'result
           (cons key
             ;; save the list of buffers
             (list (cons "buffers"
             (list
              (mapcar 'buffer-name (seq-filter 'perspectives-buffer-name-p (persp-buffers persp)))))))))))
    ;; return a different variable name so perspectives doesn't clobber it
    (cons 'perspectives-hash-serialized result)))

  ;; serialize perspectives hash
  (add-to-list 'frameset-filter-alist '(perspectives-hash . perspectives-hash-filter))
  ;; don't serialize anything else
  (add-to-list 'frameset-filter-alist '(persp-modestring . :never))
  (add-to-list 'frameset-filter-alist '(persp-recursive . :never))
  (add-to-list 'frameset-filter-alist '(persp-last . :never))
  (add-to-list 'frameset-filter-alist '(persp-curr . :never))

  (defun perspectives-restore-state ()
    (dolist (frame (frame-list))
      ;; get the serialized state off of the frame
      (let ((state (frame-parameter frame 'perspectives-hash-serialized)))
    (if state (progn
            (message "Found state, attempting restore")
            ;; delete it so we don't end up in a loop
            (set-frame-parameter frame 'perspectives-hash-serialized nil)
            (with-selected-frame frame
              (dolist (elem state)
            ;; recreate the perspective
            (with-perspective (car elem)
              (dolist (buffer-name (car (cdr (assoc "buffers" (cdr elem)))))
                ;; add the buffer back to the perspective
                (persp-add-buffer buffer-name)
                )))
              ))
      (message "No state found")
      )
    )))

  (add-hook 'desktop-after-read-hook 'perspectives-restore-state)

Eyebrowse Workspace & Window Management

Easy workspace creation and switching

(use-package eyebrowse
  :commands (eyebrowse-switch-to-window-config-1 eyebrowse-switch-to-window-config-2) 
  :general
  (:states '(insert normal) :keymaps 'override
    "s-1" 'eyebrowse-switch-to-window-config-1
    "s-2" 'eyebrowse-switch-to-window-config-2
    "s-3" 'eyebrowse-switch-to-window-config-3
    "s-4" 'eyebrowse-switch-to-window-config-4)
  :config 
  (setq eyebrowse-new-workspace 'dired-jump)
  (eyebrowse-mode t))  

Nameframe

Nameframe provides utility functions to manage frames by their names.

 (use-package nameframe
   :commands (nameframe-create-frame)
   :general
   ("s-p" 'nameframe-switch-frame)
   :config
   (nameframe-projectile-mode t)
   (nameframe-perspective-mode t))


;; functions for named work frames
(defun cpm/load-website ()
  (interactive)
  (persp-mode 1)
  (nameframe-create-frame "Website")
  (toggle-frame-maximized)
  (find-file "~/Dropbox/Work/projects/website/website.org")
  (magit-status))
(defun cpm/load-org-agenda-todo ()
  (interactive)
  (persp-mode 1)
  (nameframe-create-frame "Org Agenda")
  (toggle-frame-maximized)
  (cpm/jump-to-org-super-agenda)
  (split-window-right)
  (find-file "~/Dropbox/org-files/todo.org"))
 (defun cpm/load-phil101 ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "PHIL 101")
   (toggle-frame-maximized)
   (find-file "~/Dropbox/Work/projects/phil101/content/slides/lecture_outline.org")
   (split-window-right)
   (find-file "~/Dropbox/Work/projects/phil101/content/pages/schedule.org"))
 (defun cpm/load-phil105 ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "PHIL 105")
   (toggle-frame-maximized)
   (find-file "~/Dropbox/Work/projects/phil105/phil105-classplan.org")
   (magit-status))
 (defun cpm/load-phil232 ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "PHIL 232")
   (toggle-frame-maximized)
   (find-file "~/Dropbox/Work/projects/phil232/phil232_lecture_outline.org")
   (split-window-right)
   (find-file "~/Dropbox/Work/projects/phil232/content/pages/schedule.org"))
  (defun cpm/load-phil871 ()
    (interactive)
    (persp-mode 1)
    (nameframe-create-frame "PHIL 871")
    (toggle-frame-maximized)
    (find-file "~/Dropbox/Work/projects/phil871-kant-survey/phil871-kant-survey.org"))
 (defun cpm/load-kant-apperception-substance ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "Apperception & Substance")
   (toggle-frame-maximized)
   (org-open-link-from-string "[[file:~/Dropbox/org-files/todo.org::*Apperception%20&%20Substance]]")
   (find-file "~/Dropbox/Work/projects/KantApperception/Introspection-and-self-consciousness.org")
   (find-file "~/Dropbox/Work/projects/KantApperception/ApperceptionNotes.org")
   (find-file "~/Dropbox/Work/projects/KantApperception/KantSelf.org")
   (magit-status))
 (defun cpm/load-kant-reflection ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "Kant on Reflection")
   (toggle-frame-maximized)
   (find-file "~/Dropbox/Work/projects/KantReflection/KantReflection.md")
   (split-window-right)
   (find-file "~/Dropbox/Work/projects/KantReflection/Kant-reflection-notes.org"))
 (defun cpm/load-kant-agency-book ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "Kant on Rational Agency")
   (toggle-frame-maximized)
   (find-file "~/Dropbox/Work/projects/Kant-Agency-Book/Kant-Rational-Agency-Notes.org")
   (magit-status))
 (defun cpm/load-emacs-config ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "Emacs Config")
   (toggle-frame-maximized)
   (find-file "~/.emacs.d/config.org"))
 (defun cpm/load-kant-free-thought ()
   (interactive)
   (persp-mode 1)
   (nameframe-create-frame "Kant on Free Intellect")
   (toggle-frame-maximized)
   (find-file "~/Dropbox/Work/projects/KantFreeThought/KantFreeThought.md")
   (split-window-right)
   (find-file "~/Dropbox/Work/projects/KantFreeThought/kant-notes-intellectual-freedom.org"))

Search

Ag

(use-package ag
:commands (helm-do-ag cpm/helm-files-do-ag cpm/helm-files-search-current-directory helm-do-ag-this-file helm-ag-buffers)
:config
(progn
  (defun ag/jump-to-result-if-only-one-match ()
    "Jump to the first ag result if that ag search came up with just one match."
    (let (only-one-match)
      (when (member "--stats" ag-arguments)
        (save-excursion
          (goto-char (point-min))
          (setq only-one-match (re-search-forward "^1 matches\\s-*$" nil :noerror)))
        (when only-one-match
          (next-error)
          (kill-buffer (current-buffer))
          (message (concat "ag: Jumping to the only found match and "
                           "killing the *ag* buffer."))))))
  (add-hook 'ag-search-finished-hook #'ag/jump-to-result-if-only-one-match)  
 
  ;; Set default ag arguments
  ;; It looks like the ~/.agignore is used when launching ag from emacs too.
  ;; So the ignores from ~/.agignore don't have to be set here again.

  (setq ag-highlight-search t)
  ;; By default, ag.el will open results in a different window in the frame, so
  ;; the results buffer is still visible. You can override this so the results
  ;; buffer is hidden and the selected result is shown in its place:
  (setq ag-reuse-window nil)
  ;; reuse the same *ag* buffer for all your searches
  (setq ag-reuse-buffers t)
  ;; ;; To save buffer automatically when `wgrep-finish-edit'
  ;; (setq wgrep-auto-save-buffer t)

  (with-eval-after-load 'projectile
    ;; Override the default function to use the projectile function instead
    (defun ag/project-root (file-path)
      (let ((proj-name (projectile-project-root)))
        (if proj-name
            proj-name ; return `projectile-project-root' if non-nil
          ;; Else condition is same as the `ag/project-root' definition
          ;; from ag.el
          (if ag-project-root-function
              (funcall ag-project-root-function file-path)
            (or (ag/longest-string
                 (vc-git-root file-path)
                 (vc-svn-root file-path)
                 (vc-hg-root file-path))
                file-path))))))))

Ripgrep (rg)

An interface for ripgrep

(use-package deadgrep
  :ensure t
  :general
  (:states '(normal motion visual insert emacs)
  :keymaps 'override
  :prefix "SPC"
  :non-normal-prefix "C-SPC"
  "sg" #'deadgrep)
  (:states '(normal motion visal)
   :keymaps 'deadgrep-mode-map
   "j" #'deadgrep-forward
   "k" #'deadgrep-backward
   "l" #'deadgrep-visit-result))
;; (use-package rg
;;   :commands rg)

Anzu

anzu.el is an Emacs port of anzu.vim. anzu.el provides a minor mode which displays current match and total matches information in the mode-line in various search modes.

(use-package anzu
  :commands (isearch-foward isearch-backward)
  :config (global-anzu-mode))
(use-package evil-anzu
  :commands (evil-search-forward evil-search-backward))

Shell

Sane term

Sane settings for ansi-term

(use-package sane-term
  :commands sane-term
  :init
  ;; shell to use for sane-term
  (setq sane-term-shell-command "/usr/local/bin/zsh")
  ;; sane-term will create first term if none exist
  (setq sane-term-initial-create t)
  ;; `C-d' or `exit' will kill the term buffer.
  (setq sane-term-kill-on-exit t)
  ;; After killing a term buffer, not cycle to another.
  (setq sane-term-next-on-kill nil))

Shell Pop

A popup shell

(use-package shell-pop
  :commands shell-pop
  :init
  (setq shell-pop-term-shell "/usr/local/bin/zsh")
  (setq shell-pop-shell-type '("eshell" "*eshell*" (lambda nil (eshell))))
  :config
    (defun ansi-term-handle-close ()
     "Close current term buffer when `exit' from term buffer."
     (when (ignore-errors (get-buffer-process (current-buffer)))
       (set-process-sentinel (get-buffer-process (current-buffer))
                             (lambda (proc change)
                               (when (string-match "\\(finished\\|exited\\)" change)
                                 (kill-buffer (when (buffer-live-p (process-buffer proc)))
                                 (delete-window))))))
   (add-hook 'shell-pop-out-hook 'kill-this-buffer)
   (add-hook 'term-mode-hook (lambda () (linum-mode -1) (ansi-term-handle-close)))))

Shell settings

Other useful shell settings

 ;; basic settings
 ;; (evil-set-initial-state 'term-mode 'emacs)
 (setq explicit-shell-file-name "/usr/local/bin/zsh")
 ;; don't add newline in long lines
 (setq-default term-suppress-hard-newline t)
 ;; kill process buffers without query
 (setq kill-buffer-query-functions (delq 'process-kill-buffer-query-function kill-buffer-query-functions))
 ;; (global-set-key (kbd "C-x k") 'kill-this-buffer) 
 ;; kill ansi-buffer on exit
 (defadvice term-sentinel (around my-advice-term-sentinel (proc msg))
   (if (memq (process-status proc) '(signal exit))
       (let ((buffer (process-buffer proc)))
          ad-do-it
          (kill-buffer buffer))
         ad-do-it))
       (ad-activate 'term-sentinel)

;; clickable links & no highlight of line
(defun my-term-hook ()
  (goto-address-mode) (global-hl-line-mode 0))
(add-hook 'term-mode-hook 'my-term-hook)
 
;; paste and navigation
(defun term-send-tab ()
"Send tab in term mode."
  (interactive)
  (term-send-raw-string "\t"))

;; Emacs doesn’t handle less well, so use cat instead for the shell pager 
(setenv "PAGER" "cat")

;; hack to fix pasting issue, the paste micro-state won't work in term
(general-define-key :states '(normal) :keymaps 'term-raw-map 
       "p" 'term-paste
       "C-k" 'term-send-up
       "C-j" 'term-send-down)

(general-define-key :states '(insert) :keymaps 'term-raw-map 
       "C-c C-d" 'term-send-eof
       "C-c C-z" 'term-stop-subjob
       "<tab>"   'term-send-tab
       "s-v"     'term-paste
       "C-k"     'term-send-up
       "C-j"     'term-send-down)

Compilation buffer

Whenever I run compile, the buffer stays even after a successful compilation. Let’s make it close automatically if the compilation is successful.

(setq compilation-finish-functions
      (lambda (buf str)
        (if (null (string-match ".*exited abnormally.*" str))
            ;;no errors, make the compilation window go away in a few seconds
            (progn
              (run-at-time "0.4 sec" nil
                           (lambda ()
                             (select-window (get-buffer-window (get-buffer-create "*compilation*")))
                             (switch-to-buffer nil)
                             (delete-window)))
              (message "No Compilation Errors!")))))

Completion buffer

Kill the completion buffer

;; Remove completion buffer when done
(add-hook 'minibuffer-exit-hook 
'(lambda ()
         (let ((buffer "*Completions*"))
           (and (get-buffer buffer)
            (kill-buffer buffer)))))

Virtualenvwrapper

(use-package virtualenvwrapper
 :after (:any eshell sane-term ansi-term)
 :config
 (venv-initialize-interactive-shells) ;; if you want interactive shell support
 (venv-initialize-eshell) ;; if you want eshell support
 (setq venv-location "~/bin/virtualenvs")
 (setq venv-project-home "~/Dropbox/Work/projects/")
 (add-hook 'venv-postactivate-hook (lambda () (workon-venv))))

(defcustom venv-project-home
  (expand-file-name (or (getenv "PROJECT_HOME") "~/Dropbox/Work/projects/"))
    "The location(s) of your virtualenv projects."
    :group 'virtualenvwrapper)

(defun workon-venv ()
 "change directory to project in eshell"
  (eshell/cd (concat venv-project-home venv-current-name)))

Tramp

An easy way to ssh

(use-package tramp-term
  :commands tramp-term
)

Eshell

Eshell is an elisp shell. It has its own configuration parameters, distinct from those of shell or ansi-terminal.

Eshell Settings

Basic settings

(use-package eshell
  :commands eshell
  :init
  (setq eshell-directory-name (concat cpm-etc-dir "eshell/")
        eshell-history-file-name (concat cpm-etc-dir "eshell/history")
        eshell-aliases-file (concat cpm-etc-dir "eshell/alias")
        eshell-last-dir-ring-file-name (concat cpm-etc-dir "eshell/lastdir")
        eshell-highlight-prompt nil
        eshell-buffer-shorthand t
        eshell-cmpl-ignore-case t
        eshell-cmpl-cycle-completions t
        eshell-destroy-buffer-when-process-dies t
        eshell-history-size 10000
        ;; auto truncate after 20k lines
        eshell-buffer-maximum-lines 20000
        eshell-hist-ignoredups t
        eshell-error-if-no-glob t
        eshell-glob-case-insensitive t
        eshell-scroll-to-bottom-on-input 'all
        eshell-scroll-to-bottom-on-output 'all
        eshell-list-files-after-cd t
        eshell-banner-message ""
        ;; eshell-banner-message (message "Emacs initialized in %.2fs \n\n" (float-time (time-subtract (current-time) my-start-time)))
        ;; eshell-banner-message "What would you like to do?\n\n"
      )
      ;; Visual commands
  (setq eshell-visual-commands '("ranger" "vi" "screen" "top" "less" "more" "lynx"
                                     "ncftp" "pine" "tin" "trn" "elm" "vim"
                                     "nmtui" "alsamixer" "htop" "el" "elinks"
                                     ))
  (setq eshell-visual-subcommands '(("git" "log" "diff" "show"))))



(defun cpm/setup-eshell ()
 (interactive)
  ;; turn off semantic-mode in eshell buffers
  (semantic-mode -1)
  ;; turn off hl-line-mode
  (hl-line-mode -1))

Eshell Helm

;; helm support
(add-hook 'eshell-mode-hook
     (lambda ()
       (eshell-cmpl-initialize)
       (define-key eshell-mode-map [remap eshell-pcomplete] 'helm-esh-pcomplete)
       (define-key eshell-mode-map (kbd "M-l") 'helm-eshell-history)
       (cpm/setup-eshell)))

    (when (not (functionp 'eshell/rgrep))
      (defun eshell/rgrep (&rest args)
        "Use Emacs grep facility instead of calling external grep."
        (eshell-grep "rgrep" args t)))

Eshell Truncate Buffers

Per this stack exchange discussion

(defun my/truncate-eshell-buffers ()
  "Truncates all eshell buffers"
  (interactive)
  (save-current-buffer
    (dolist (buffer (buffer-list t))
      (set-buffer buffer)
      (when (eq major-mode 'eshell-mode)
        (eshell-truncate-buffer)))))

;; After being idle for 5 seconds, truncate all the eshell-buffers if
;; needed. If this needs to be canceled, you can run `(cancel-timer
;; my/eshell-truncate-timer)'
(setq my/eshell-truncate-timer
      (run-with-idle-timer 5 t #'my/truncate-eshell-buffers))

Eshell Evil History Navigation

History browsing. Note keybindings need to be buffer local as per https://github.com/noctuid/general.el/issues/80

(add-hook 'eshell-mode-hook
(lambda ()
(general-define-key :states  '(normal insert emacs) :keymaps 'eshell-mode-map
    "<down>" 'eshell-next-input
    "<up>"   'eshell-previous-input
    "C-k"    'eshell-next-input
    "C-j"    'eshell-previous-input)
    ))

Eshell Prompt

A nicer eshell prompt with some useful discussion of how it was put together. I’ve made just a few tiny modifications.

(require 'dash)
(require 's)

(defmacro with-face (STR &rest PROPS)
  "Return STR propertized with PROPS."
  `(propertize ,STR 'face (list ,@PROPS)))

(defmacro esh-section (NAME ICON FORM &rest PROPS)
  "Build eshell section NAME with ICON prepended to evaled FORM with PROPS."
  `(setq ,NAME
         (lambda () (when ,FORM
                 (-> ,ICON
                    (concat esh-section-delim ,FORM)
                    (with-face ,@PROPS))))))

(defun esh-acc (acc x)
  "Accumulator for evaluating and concatenating esh-sections."
  (--if-let (funcall x)
      (if (s-blank? acc)
          it
        (concat acc esh-sep it))
    acc))

(defun esh-prompt-func ()
  "Build `eshell-prompt-function'"
  (concat esh-header
          (-reduce-from 'esh-acc "" eshell-funcs)
          "\n"
          eshell-prompt-string))

(esh-section esh-dir
             "\xf07c"  ;  (faicon folder)
             (abbreviate-file-name (eshell/pwd))
             '(:foreground "#268bd2" :underline t))

(esh-section esh-git
             "\xe907"  ;  (git icon)
             (with-eval-after-load 'magit
             (magit-get-current-branch))
             '(:foreground "#b58900"))

(esh-section esh-python
             "\xe928"  ;  (python icon)
             (with-eval-after-load "virtualenvwrapper"
             venv-current-name))

(esh-section esh-clock
             "\xf017"  ;  (clock icon)
             (format-time-string "%H:%M" (current-time))
             '(:foreground "forest green"))

;; Below I implement a "prompt number" section
(setq esh-prompt-num 0)
(add-hook 'eshell-exit-hook (lambda () (setq esh-prompt-num 0)))
(advice-add 'eshell-send-input :before
            (lambda (&rest args) (setq esh-prompt-num (incf esh-prompt-num))))

(esh-section esh-num
             "\xf0c9"  ;  (list icon)
             (number-to-string esh-prompt-num)
             '(:foreground "brown"))

;; Separator between esh-sections
(setq esh-sep " | ")  ; or "  "

;; Separator between an esh-section icon and form
(setq esh-section-delim " ")

;; Eshell prompt header
(setq esh-header "\n┌─")  ; or "\n "

;; Eshell prompt regexp and string. Unless you are varying the prompt by eg.
;; your login, these can be the same.
(setq eshell-prompt-regexp "^└─>> ") ;; note the '^' to get regex working right
(setq eshell-prompt-string "└─>> ") 

;; Choose which eshell-funcs to enable
(setq eshell-funcs (list esh-dir esh-git esh-python esh-clock esh-num))

;; Enable the new eshell prompt
(setq eshell-prompt-function 'esh-prompt-func)

Shell Switcher

Useful for switching between multiple instances of eshell. But you can configure for any shell that you use.

(use-package shell-switcher
  :general
  ("C-'"  'shell-switcher-switch-buffer-other-window)
  :config
  (add-hook 'eshell-mode-hook 'shell-switcher-manually-register-shell)
  (setq shell-switcher-mode t))

Clear Eshell

Make eshell act like a standard unix terminal.

  (defun eshell-clear-buffer ()
  "Clear terminal"
  (interactive)
  (let ((inhibit-read-only t))
    (erase-buffer)
    (eshell-send-input)))
(add-hook 'eshell-mode-hook
      '(lambda()
          (local-set-key (kbd "C-l") 'eshell-clear-buffer)))

Eshell Magit

(defun eshell/magit ()
"Function to open magit-status for the current directory"
  (interactive)
  (magit-status default-directory)
  nil)

Eshell Fringe Status

Show last status in fringe

(use-package eshell-fringe-status
  :defer t
  :config
  (add-hook 'eshell-mode-hook 'eshell-fringe-status-mode))

Eshell Autosuggest

Fish-like history autosuggestions in eshell

(use-package esh-autosuggest
  :hook (eshell-mode . esh-autosuggest-mode))

Version Control

Magit is a great interface for git projects. It’s much more pleasant to use than the standard git interface on the command line. I’ve set up some easy keybindings to access magit and related packages.

Magit

(use-package magit
  :commands 
  (magit-blame-mode
   magit-commit
   magit-diff
   magit-log
   magit-status)
  :init
  (add-hook 'git-commit-mode-hook 'turn-on-flyspell)
  (add-hook 'magit-mode-hook 'evil-magit-init)
  (setq vc-follow-symlinks t)
  ;; Suppress the message we get about "Turning on
  ;; magit-auto-revert-mode" when loading Magit.
  (setq magit-no-message '("Turning on magit-auto-revert-mode..."))
  :config
  ;; make magit go fullscreen
  ;; (setq magit-display-buffer-function #'magit-display-buffer-fullframe-status-v1)
  (setq magit-diff-refine-hunk 'all)
  (global-git-commit-mode t) ; use emacs as editor for git commits
  (setq magit-push-always-verify nil)
  )

Evil Magit

Evil bindings for magit

(use-package evil-magit
  :after magit
  :demand t
  :config
  (setq evil-magit-use-y-for-yank t))

Magithub

Magit & Github = Awesome
(use-package magithub
  :ensure t
  :after magit
  :config
  (magithub-feature-autoinject t)
  (setq magithub-clone-default-directory "~/.emacs.d/.local/elisp/"))

Helm Gitignore

(use-package helm-gitignore
  :ensure t
  :commands helm-gitignore)

Git Commit

(use-package git-commit
  :ensure t
  :after magit
  :hook (git-commit-mode . cpm/git-commit-auto-fill-everywhere)
  :custom (git-commit-summary-max-length 50)
  :preface
  (defun cpm/git-commit-auto-fill-everywhere ()
    "Ensures that the commit body does not exceed 72 characters."
    (setq fill-column 72)
    (setq-local comment-auto-fill-only-comments nil)))

Git Auto Commit

Automatically commit to git after each save
(use-package git-auto-commit-mode
  :ensure t
  :commands git-auto-commit-mode
  :config
  (setq gac-automatically-push-p 1
        gac-ask-for-summary-p nil))

Git timemachine

(use-package git-timemachine            ; Go back in Git time
  :commands git-timemachine)

Gited

Give git projects branches the dired treatment

(use-package gited
  :commands (gited-list gited-list-branches))

Git gutter

Git gutter is great for giving visual feedback on changes, but it doesn’t play well with org-mode using org-indent. So I don’t use it globally.

(use-package git-gutter
  :defer t
  :init
  (add-hook! (markdown-mode prog-mode conf-mode) 'git-gutter-mode)
  :config
  (setq git-gutter:disabled-modes '(org-mode asm-mode image-mode)
        git-gutter:update-interval 2
        git-gutter:window-width 2))

(use-package git-gutter-fringe
  :diminish git-gutter-mode
  :after git-gutter
  ;; :demand fringe-helper
  :config
  ;; subtle diff indicators in the fringe
  ;; places the git gutter outside the margins.
  (setq-default fringes-outside-margins t)
  ;; thin fringe bitmaps
  (define-fringe-bitmap 'git-gutter-fr:added
  [224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224]
  nil nil 'center)
  (define-fringe-bitmap 'git-gutter-fr:modified
  [224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224]
  nil nil 'center)
  (define-fringe-bitmap 'git-gutter-fr:deleted
  [0 0 0 0 0 0 0 0 0 0 0 0 0 128 192 224 240 248]
  nil nil 'center))

Quick commits

Make a quick commit without opening magit. This is a version of a workflow I used to use in Sublime Text. Perfect for short commit messages.

(defun quick-commit ()
"make a quick commit from the mini-buffer"
(interactive)
(evil-ex '"!Git add % && Git commit -m '" ))

Org Mode

Settings

New Org

Ensure ELPA org is prioritized above built-in org.

(setq load-path (remove-if (lambda (x) (string-match-p "org$" x)) load-path))

Org Directories

(setq org-directory "~/Dropbox/org-files")
(setq org-default-notes-file (concat org-directory "/org-notes.org"))

State Settings

(setq org-todo-keywords
 '((sequence "TODO" "NEXT" "|" "DONE")
   (sequence "INPROGRESS" "WAITING(w@/!)" "|" "CANCELED" "INACTIVE")))

Priority Settings

(setq org-priority-faces '((?A . (:foreground "red" :weight 'bold))
                           (?B . (:foreground "orange"))
                           (?C . (:foreground "yellow"))
                           (?D . (:forefround "green"))))

Org Logging

Log timestamp when task is marked “DONE” or other finished keyword

(setq org-log-done 'time)

Org Babel Languages

(with-eval-after-load 'org
(org-babel-do-load-languages
 'org-babel-load-languages
 '((latex . t)
   (lisp . t)
   (shell . t)))

(defun cpm/org-confirm-babel-evaluate (lang body)
  "Do not confirm evaluation for these languages."
  (not (or (string= lang "emacs-lisp"))))
        
(setq org-confirm-babel-evaluate 'my-org-confirm-babel-evaluate))

Org Babel Source Blocks

(setq org-src-fontify-natively t
      org-src-window-setup 'other-window
      org-src-tab-acts-natively nil
      org-src-strip-leading-and-trailing-blank-lines t)

Org Config Settings

(setq-default 
      org-footnote-section nil ;; place footnotes locally rather than in own section
      org-return-follows-link t ;; make RET follow links
      org-list-allow-alphabetical t ;; allow alphabetical list
      org-hide-emphasis-markers t  ;; hide markers
      org-pretty-entities t ;; make latex look good
      org-pretty-entities-include-sub-superscripts t
      org-hide-leading-stars t
      org-export-with-smart-quotes t ;; export smart quote marks
      org-refile-use-cache t  ;; use cache for org refile
      org-startup-folded t
      org-yank-adjusted-subtrees t  ;; adjust subtrees to depth when yanked
      org-yank-folded-subtrees t  ;; fold subtrees on yank
      org-M-RET-may-split-line '((default . nil))  ;; don't split line when creating a new headline, list item, or table field
      org-fontify-quote-and-verse-blocks t ;; make quotes stand out
      org-table-export-default-format "orgtbl-to-csv" ;; export for org-tables to csv
      ;; org-ellipsis "↷" ;; nicer elipses "↴" "▼"
      org-startup-indented t ;; start in indent mode
      org-imenu-depth 8
      imenu-auto-rescan t)

Org Modules

A list of org modules to load on startup

(setq org-modules (quote (org-info org-protocol org-habit org-mac-link)))

Org ID

(setq org-id-locations-file (concat cpm-cache-dir ".org-id-locations"))

Org Regex (Emphasis)

Per this stack exchange discussion.

(with-eval-after-load 'org
; chars for prematch
(setcar org-emphasis-regexp-components            "     ('\"{“”\[\\\_\-") 
; chars for postmatch
(setcar (nthcdr 1 org-emphasis-regexp-components) "\] -   .,!?;:''“”\")}/\\“”\_\-") 
; forbidden chars
(setcar (nthcdr 2 org-emphasis-regexp-components) "    \t\r\n,\"")
; body
(setcar (nthcdr 3 org-emphasis-regexp-components) ".")
; max newlines 
(setcar (nthcdr 4 org-emphasis-regexp-components) 1)
(org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components))

Org Entities

Display proper utf-8 characters

(setq org-entities-user
      '(("nec" "\Box" nil "" "" "" "")
        ("pos" "\Diamond" nil "" "" "" "")))

Hooks

(add-hook 'org-mode-hook
    (lambda () 
    (turn-on-visual-fill-column-mode) 
    (centered-cursor-mode)
      ;; (turn-on-auto-fill)
      ))

Org Archive

Tell org where to archive completed tasks

(setq org-archive-location (concat org-directory "/org-archive/archived.org::datetree/"))

Also tell org how to archive all the done tasks (DONE or CANCELED) in a file. From here based on a stack overflow answer

(defun cpm/org-archive-done-tasks ()
  (interactive)
  (org-map-entries
   (lambda ()
     (org-archive-subtree)
     (setq org-map-continue-from (outline-previous-heading)))
   "/DONE" 'file)
  (org-map-entries
   (lambda ()
     (org-archive-subtree)
     (setq org-map-continue-from (outline-previous-heading)))
   "/CANCELED" 'file)
)

Org Refile

Set refile settings. I got a lot of help on this from Aaron Bieber’s discussion.

With this set, you can trigger Refile with C-c C-w in any Org file and get a completing read of all headings up to three levels deep in all files in org-agenda-files. You can also refile to the top header in a document and create new parents.

(setq org-refile-targets '((org-agenda-files :maxlevel . 3)
                           ("/Users/roambot/.emacs.d/config.org" :maxlevel . 8)))
(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)
(setq org-refile-allow-creating-parent-nodes 'confirm)

Open Files In Default Application

Open files in their default applications (ms word being the prime example)

(setq org-file-apps
      '(("\\.docx\\'" . default)
        ("\\.mm\\'" . default)
        ("\\.x?html?\\'" . default)
        ("\\.pdf\\'" . default)
        (auto-mode . emacs)))

Org-Agenda

Agenda Settings

Settings for the agenda.

(setq org-agenda-files '("~/Dropbox/org-files/"))
(general-define-key "C-c a" #'org-agenda)

;; Display properties
(setq org-cycle-separator-lines 0
      org-tags-column 80
      org-agenda-tags-column org-tags-column
      org-agenda-window-setup 'only-window
      org-agenda-restore-windows-after-quit t
      org-agenda-todo-ignore-scheduled nil
      org-agenda-todo-ignore-deadlines nil 
      ;; org-agenda-sticky t
      org-agenda-span 'day)

(with-eval-after-load 'org-agenda
  (general-define-key :keymaps 'org-agenda-mode-map 
                      "j" 'org-agenda-next-item
                      "k" 'org-agenda-previous-item))

;; automatically refresh the agenda after adding a task
(defun cpm/org-agenda-refresh ()
(interactive)
(when (get-buffer "*Org Agenda*")
    (with-current-buffer "*Org Agenda*"
        (org-agenda-redo t)
        (message "[org agenda] refreshed!"))))
(add-hook 'org-capture-after-finalize-hook 'cpm/org-agenda-refresh)

;; show all todos
(defun cpm/jump-to-org-agenda-all-todos ()
  "open agenda with all unscheduled/non-deadline todos"
  (interactive)
  (org-agenda nil "z"))

;; jump to week agenda
(defun cpm/jump-to-week-agenda ()
  "open custom week agenda"
  (interactive)
  (org-agenda nil "W"))

Agenda Navigation

Courtesy of Aaron Bieber

(defun air-org-agenda-next-header ()
  "Jump to the next header in an agenda series."
  (interactive)
  (air--org-agenda-goto-header))

(defun air-org-agenda-previous-header ()
  "Jump to the previous header in an agenda series."
  (interactive)
  (air--org-agenda-goto-header t))

(defun air--org-agenda-goto-header (&optional backwards)
  "Find the next agenda series header forwards or BACKWARDS."
  (let ((pos (save-excursion
               (goto-char (if backwards
                              (line-beginning-position)
                            (line-end-position)))
               (let* ((find-func (if backwards
                                     'previous-single-property-change
                                   'next-single-property-change))
                      (end-func (if backwards
                                    'max
                                  'min))
                      (all-pos-raw (list (funcall find-func (point) 'org-agenda-structural-header)
                                         (funcall find-func (point) 'org-agenda-date-header)))
                      (all-pos (cl-remove-if-not 'numberp all-pos-raw))
                      (prop-pos (if all-pos (apply end-func all-pos) nil)))
                 prop-pos))))
    (if pos (goto-char pos))
    (if backwards (goto-char (line-beginning-position)))))
 
(with-eval-after-load 'org-agenda
(general-define-key :keymaps 'org-agenda-mode-map :states '(normal motion)
  "J" 'air-org-agenda-next-header
  "K" 'air-org-agenda-previous-header))

Org Super-Agenda

Supercharge org-agenda. Settings courtesy of alphapapa.

(use-package org-super-agenda
 :general 
 (:states '(normal motion emacs) :keymaps 'org-agenda-keymap 
 ","  'cpm/hydra-org-agenda/body)
 :config
 (org-super-agenda-mode +1) 
  ;; see https://github.com/alphapapa/org-super-agenda/issues/11
  (org-super-agenda--defgroup time-grid
  "Group items that appear on a time grid. This matches the `dotime' text-property, which, if NOT set to `time' (I know, this gets confusing), means it WILL appear in the agenda time-grid. "
  :section-name "Timed items"  ; Note: this does not mean the item has a "SCHEDULED:" line
  :test (or (--when-let (org-find-text-property-in-string 'time item)
              ;; This property is a string; if empty, it doesn't match
              (not (string-empty-p it)))
            ;; This property is nil if it doesn't match
            (org-find-text-property-in-string 'time-of-day item)
            (--when-let (org-find-text-property-in-string 'dotime item)
              ;; For this to match, the 'dotime property must be set, and
              ;; it must not be equal to 'time.  If it is not set, or if
              ;; it is set and is equal to 'time, the item is not part of
              ;; the time-grid.  Yes, this is confusing.  :)
              (not (eql it 'time))))))

Hydra for Agenda

Hydra for org agenda (graciously offered by Spacemacs)

(after! org-agenda
(org-super-agenda-mode)
(defhydra cpm/hydra-org-agenda (:color pink :hint none)
  "
Org agenda (_q_uit)

^Clock^      ^Visit entry^              ^Date^             ^Other^
^-----^----  ^-----------^------------  ^----^-----------  ^-----^---------
_ci_ in      _SPC_ in other window      _ds_ schedule      _gr_ reload
_co_ out     _TAB_ & go to location     _dd_ set deadline  _._  go to today
_cq_ cancel  _RET_ & del other windows  _dt_ timestamp     _gd_ go to date
_cj_ jump    _o_   link                 _+_  do later      ^^
^^           ^^                         _-_  do earlier    ^^
^^           ^^                         ^^                 ^^
^View^          ^Filter^                 ^Headline^         ^Toggle mode^
^----^--------  ^------^---------------  ^--------^-------  ^-----------^----
_vd_ day        _ft_ by tag              _ht_ set status    _tf_ follow
_vw_ week       _fr_ refine by tag       _hk_ kill          _tl_ log
_vt_ fortnight  _fc_ by category         _hr_ refile        _ta_ archive trees
_vm_ month      _fh_ by top headline     _hA_ archive       _tA_ archive files
_vy_ year       _fx_ by regexp           _h:_ set tags      _tr_ clock report
_vn_ next span  _fd_ delete all filters  _hp_ set priority  _td_ diaries
_vp_ prev span  ^^                       ^^                 ^^
_vr_ reset      ^^                       ^^                 ^^
^^              ^^                       ^^                 ^^
"
  ;; Entry
  ("hA" org-agenda-archive-default)
  ("hk" org-agenda-kill)
  ("hp" org-agenda-priority)
  ("hr" org-agenda-refile)
  ("h:" org-agenda-set-tags)
  ("ht" org-agenda-todo)
  ;; Visit entry
  ("o"   link-hint-open-link :exit t)
  ("<tab>" org-agenda-goto :exit t)
  ("TAB" org-agenda-goto :exit t)
  ("SPC" org-agenda-show-and-scroll-up)
  ("RET" org-agenda-switch-to :exit t)
  ;; Date
  ("dt" org-agenda-date-prompt)
  ("dd" org-agenda-deadline)
  ("+" org-agenda-do-date-later)
  ("-" org-agenda-do-date-earlier)
  ("ds" org-agenda-schedule)
  ;; View
  ("vd" org-agenda-day-view)
  ("vw" org-agenda-week-view)
  ("vt" org-agenda-fortnight-view)
  ("vm" org-agenda-month-view)
  ("vy" org-agenda-year-view)
  ("vn" org-agenda-later)
  ("vp" org-agenda-earlier)
  ("vr" org-agenda-reset-view)
  ;; Toggle mode
  ("ta" org-agenda-archives-mode)
  ("tA" (org-agenda-archives-mode 'files))
  ("tr" org-agenda-clockreport-mode)
  ("tf" org-agenda-follow-mode)
  ("tl" org-agenda-log-mode)
  ("td" org-agenda-toggle-diary)
  ;; Filter
  ("fc" org-agenda-filter-by-category)
  ("fx" org-agenda-filter-by-regexp)
  ("ft" org-agenda-filter-by-tag)
  ("fr" org-agenda-filter-by-tag-refine)
  ("fh" org-agenda-filter-by-top-headline)
  ("fd" org-agenda-filter-remove-all)
  ;; Clock
  ("cq" org-agenda-clock-cancel)
  ("cj" org-agenda-clock-goto :exit t)
  ("ci" org-agenda-clock-in :exit t)
  ("co" org-agenda-clock-out)
  ;; Other
  ("q" nil :exit t)
  ("gd" org-agenda-goto-date)
  ("." org-agenda-goto-today)
  ("gr" org-agenda-redo))

  (defun cpm/jump-to-org-super-agenda ()
  (interactive)
  (org-agenda nil "s")))

Agenda Custom Commands

(defun air-org-skip-subtree-if-habit ()
  "Skip an agenda entry if it has a STYLE property equal to \"habit\"."
  (let ((subtree-end (save-excursion (org-end-of-subtree t))))
    (if (string= (org-entry-get nil "STYLE") "habit")
        subtree-end
      nil)))

(defun air-org-skip-subtree-if-priority (priority)
  "Skip an agenda subtree if it has a priority of PRIORITY.

PRIORITY may be one of the characters ?A, ?B, or ?C."
  (let ((subtree-end (save-excursion (org-end-of-subtree t)))
        (pri-value (* 1000 (- org-lowest-priority priority)))
        (pri-current (org-get-priority (thing-at-point 'line t))))
    (if (= pri-value pri-current)
        subtree-end
      nil)))
; https://orgmode.org/manual/Storing-searches.html#Storing-searches
(setq org-agenda-custom-commands
      '(("x" agenda)
        ("y" agenda*) ; or agenda entries planned this week/day with an hour specification like [h]h:mm
        ("z" todo "TODO")
        ("i" todo "INPROGRESS")
        ("n" todo "NEXT")
        ("r" todo "REVISE")
        ("w" todo "WAITING")
        ;; ("W" "Week view"
        ;;  ((agenda "" ((org-agenda-span 'week)
        ;;               (org-super-agenda-groups
        ;;                '((:name "Schedule"
        ;;                         :time-grid t)
        ;;                  (:name "Today"
        ;;                         :scheduled today)
        ;;                  (:name "Due Today"
        ;;                         :deadline today
        ;;                         :order 2)
        ;;                  (:name "Due Soon"
        ;;                         :deadline future
        ;;                         :order 8)
        ;;                  (:name "Overdue"
        ;;                  :deadline past
        ;;                         :order 7)))))
        ;;   ))

        ("W" "Week's agenda and all TODOs"
         ((tags "PRIORITY=\"A\""
                ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                 (org-agenda-overriding-header "High-priority unfinished tasks:")))
          (agenda "" ((org-agenda-span 'week)))
          (alltodo ""
                   ((org-agenda-skip-function '(or (air-org-skip-subtree-if-habit)
                                                   (air-org-skip-subtree-if-priority ?A)
                                                   (org-agenda-skip-if nil '(scheduled deadline))))
                    (org-agenda-overriding-header "ALL normal priority tasks:"))))
         ((org-agenda-compact-blocks nil)))

        ("s" "Super Agenda"
         ((agenda "" ((org-agenda-span 'day)
                      (org-super-agenda-groups
                       '((:name "Schedule"
                                :time-grid t)
                         (:name "Today"
                                :scheduled today)
                         (:name "Due Today"
                                :deadline today
                                :order 2)
                         (:name "Due Soon"
                                :deadline future
                                :order 8)
                         (:name "Overdue"
                                :deadline past
                                :order 7)))))
          (alltodo "" ((org-agenda-overriding-header "")
                       (org-super-agenda-groups
                        '((:name "Next to do"
                                 :todo "NEXT"
                                 :order 1)
                          (:name "Important"
                                 :tag "Important"
                                 :priority "A"
                                 :order 6)
                          (:name "In progress"
                                 :todo "INPROGRESS"
                                 :order 15)
                          (:name "Waiting"
                                 :todo "WAITING"
                                 :order 20)
                          (:name "To read"
                                 :todo "TOREAD"
                                 :order 30)
                          (:name "Low priority"
                                 :priority<= "C"
                                 :todo ("SOMEDAY" )
                                 :order 90)
                          (:discard (:tag ("Chore" "Routine" "Daily")))

                          )))))
        )))

Org Capture

Capture Settings

(general-define-key "C-c c" 'org-capture)
(add-hook 'org-capture-mode-hook 'evil-insert-state)
(setq org-capture-templates
      '(("p" "Projects" entry (file+headline "~/Dropbox/org-files/todo.org" "Projects")
         "** STARTED %? \n  %i")
        ("c" "Capture" entry (file+headline "~/Dropbox/org-files/todo.org" "Inbox")
         "* TODO %?\n %i")
        ("j" "Journal" entry (file+olp+datetree "~/Dropbox/org-files/journal.org")
         "**** %<%H:%M>\n%?")
        ("l" "A link, for reading later." entry (file+headline "~/Dropbox/org-files/org-notes.org" "Reading List")
          "* %:description\n%u\n\n%c\n\n%i"
          :empty-lines 1)
        ("m" "Mail-Task" entry (file+headline "~/Dropbox/org-files/todo.org" "Inbox")
         "** TODO %:description                         :email: \n[[message://%l][Email link]] \n%? ")
        ("n" "Notes" entry (file "~/Dropbox/org-files/org-notes.org")
        "* %?")
        ("w" "Review: Weekly Review" entry (file+datetree "~/Dropbox/org-files/reviews.org")
          (file "~/Dropbox/org-files/templates/weekly_review_template.org"))))

  ;; Add date to captured items
  (defun add-property-with-date-captured ()
    "Add DATE_CAPTURED property to the current item."
    (interactive)
    (org-set-property "DATE_CAPTURED" (format-time-string "%c")))

  (add-hook 'org-capture-before-finalize-hook 'add-property-with-date-captured)

Org Journal Capture

Tell emacs what you’re doing a few times a day. Depends on a shell script run in the background. I got the idea from Diego Berrocal. Hat tip to stack overflow for help on hooks for the created frame.

(defun cpm/org-journal ()
  (interactive) (org-capture nil "j"))

(defun cpm/what-are-you-doing-capture ()
  (interactive)
  (make-frame '((name . "What are you doing?") (left . (+ 550)) (top . (+ 400)) (width . 100) (height . 12)))
  (select-frame-by-name "What are you doing?")
  (cpm/org-journal)
  )

Alfred capture workflow

Help alfred and org-capture play nice. Courtesy of worg with some slight modifications.

(defun cpm/org-capture-frame ()
  (interactive)
  (org-capture nil "c"))
  
(defun cpm/make-orgcapture-frame ()
  "Create a new frame and run org-capture."
  (interactive)
  (make-frame '((name . "alfred-capture") (width . 90) (height . 20)
                (top . 400) (left . 300)
                ))
  (select-frame-by-name "alfred-capture")
  (cpm/org-capture-frame))

Capture advice

Make capture the only window and close after refiling.

(defadvice org-capture
  (after make-full-window-frame activate)
  "Advise capture to be the only window when used as a popup"
  (cond ((equal "What are you doing?" (frame-parameter nil 'name)) (delete-other-windows))
        ((equal "alfred-capture" (frame-parameter nil 'name)) (delete-other-windows))
        ((equal "Email Capture" (frame-parameter nil 'name)) (delete-other-windows))

        ))

 (defadvice org-capture-finalize
   (after delete-capture-frame activate)
    "Advise capture-finalize to close the frame"
    (cond ((equal "What are you doing?" (frame-parameter nil 'name)) (delete-frame))
          ((equal "alfred-capture" (frame-parameter nil 'name)) (delete-frame))
          ((equal "Email Capture" (frame-parameter nil 'name)) (delete-frame))
          ))

Org Template Expansions

(with-eval-after-load 'org
(add-to-list 'org-structure-template-alist
  '("E" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))
(add-to-list 'org-structure-template-alist
  '("n" "#+BEGIN_NOTES\n?\n#+END_NOTES"))
(add-to-list 'org-structure-template-alist
  '("t" "#+BEGIN_COMMENT TODO: ?  #+END_COMMENT"))
(add-to-list 'org-structure-template-alist
  '("b" "#+REVEAL: split?"))
(add-to-list 'org-structure-template-alist
  '("f" "#+ATTR_REVEAL: :frag (appear)?")))

Org Bullets

(use-package org-bullets
  :hook (org-mode . org-bullets-mode)
  :config 
  (setq org-bullets-bullet-list '("" ""))) 

  ;; Other bullets
  ;; "●" "◉" "→"
  ;; ("◉" "◎" "⚫" "○" "►" "◇")
  ;;  "∙" "∶" "∵" "∷" "∺" )))
  ;; (setq org-bullets-bullet-list '("❂" "⁑" "⁂" "❖" "✮" "✱" "✵")))

;; Asterisks and dashes for bullet lists are fine, but actual circular bullets are better
;; via http://www.howardism.org/Technical/Emacs/orgmode-wordprocessor.html
(font-lock-add-keywords 'org-mode
                      '(("^ +\\([-*]\\) "
                         (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))

Org Prettify Source Blocks

Make source blocks look better. Courtesy of Rasmus Pank Roulund. Last updated: 2018-04-06

(with-eval-after-load 'org
  (defvar-local rasmus/org-at-src-begin -1
    "Variable that holds whether last position was a ")

  (defvar rasmus/ob-header-symbol ?☰
    "Symbol used for babel headers")

  (defun rasmus/org-prettify-src--update ()
    (let ((case-fold-search t)
          (re "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")
          found)
      (save-excursion
        (goto-char (point-min))
        (while (re-search-forward re nil t)
          (goto-char (match-end 0))
          (let ((args (org-trim
                       (buffer-substring-no-properties (point)
                                                       (line-end-position)))))
            (when (org-string-nw-p args)
              (let ((new-cell (cons args rasmus/ob-header-symbol)))
                (cl-pushnew new-cell prettify-symbols-alist :test #'equal)
                (cl-pushnew new-cell found :test #'equal)))))
        (setq prettify-symbols-alist
              (cl-set-difference prettify-symbols-alist
                                 (cl-set-difference
                                  (cl-remove-if-not
                                   (lambda (elm)
                                     (eq (cdr elm) rasmus/ob-header-symbol))
                                   prettify-symbols-alist)
                                  found :test #'equal)))
        ;; Clean up old font-lock-keywords.
        (font-lock-remove-keywords nil prettify-symbols--keywords)
        (setq prettify-symbols--keywords (prettify-symbols--make-keywords))
        (font-lock-add-keywords nil prettify-symbols--keywords)
        (while (re-search-forward re nil t)
          (font-lock-flush (line-beginning-position) (line-end-position))))))

  (defun rasmus/org-prettify-src ()
    "Hide src options via `prettify-symbols-mode'.

  `prettify-symbols-mode' is used because it has uncollpasing. It's
  may not be efficient."
    (let* ((case-fold-search t)
           (at-src-block (save-excursion
                           (beginning-of-line)
                           (looking-at "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*"))))
      ;; Test if we moved out of a block.
      (when (or (and rasmus/org-at-src-begin
                     (not at-src-block))
                ;; File was just opened.
                (eq rasmus/org-at-src-begin -1))
        (rasmus/org-prettify-src--update))
      ;; Remove composition if at line; doesn't work properly.
      ;; (when at-src-block
      ;;   (with-silent-modifications
      ;;     (remove-text-properties (match-end 0)
      ;;                             (1+ (line-end-position))
      ;;                             '(composition))))
      (setq rasmus/org-at-src-begin at-src-block)))

  (defun rasmus/org-prettify-symbols ()
    (mapc (apply-partially 'add-to-list 'prettify-symbols-alist)
          (cl-reduce 'append
                     (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
                             `(("#+begin_src" . ?╦) ;; ➤ 🖝 ➟ ➤ ✎ ✎
                               ("#+end_src"   . ?╩) ;; □
                               ("#+header:" . ,rasmus/ob-header-symbol)
                               ("#+begin_comment" . ?✎)
                               ("#+end_comment" . ?✎)
                               ("#+begin_notes" . ?➤)
                               ("#+end_notes" . ?➤)
                               ("#+begin_quote" . )
                               ("#+end_quote" . )))))
    (turn-on-prettify-symbols-mode)
    (add-hook 'post-command-hook 'rasmus/org-prettify-src t t))
  (add-hook 'org-mode-hook #'rasmus/org-prettify-symbols))

Org-Goto

Make counsel display org headings nicely.

(with-eval-after-load 'org
  (setq counsel-org-goto-display-style 'path)
  (setq counsel-org-goto-separator "")
  (setq counsel-org-goto-face-style 'org)
  (define-key org-mode-map (kbd "C-c C-j") 'counsel-org-goto)
  (define-key org-mode-map (kbd "C-u C-c C-j") 'counsel-org-goto-all))

Org-Reveal

(use-package ox-reveal
;; :commands (org-reveal-export-current-subtree org-reveal-export-to-html-and-browse)
:after ox
:config
(setq org-reveal-root (concat "file://" (getenv "HOME") "/bin/reveal.js")
      org-reveal-theme "moon"
      org-reveal-default-frag-style "roll-in"
      org-reveal-hlevel 2
      ))
   
(defun cpm/narrowed-subtree-to-html ()
  "export narrowed tree to html"
  (interactive)
  (org-reveal-export-current-subtree)
  (org-narrow-to-subtree))
  
(fset 'cpm/reveal-to-html-open
 "\C-c\C-e\C-sRB")

Org Organization (GTD)

GTD Projects

(defun cpm/org-goto-todo ()
  (interactive)
  (find-file "~/Dropbox/org-files/todo.org")
  (widen)
  (beginning-of-buffer)
  (re-search-forward "* Inbox")
  (beginning-of-line))

(defun cpm/org-goto-inbox ()
  (interactive)
  (find-file "~/Dropbox/org-files/inbox.org")
  (widen)
  (beginning-of-buffer)
  (beginning-of-line))

(defun cpm/org-goto-projects ()
  (interactive)
  (find-file "~/Dropbox/org-files/todo.org")
  (widen)
  (beginning-of-buffer)
  (re-search-forward "* Projects")
  (beginning-of-line))

(defun cpm/project-overview ()
  (interactive)
  (cpm/org-goto-projects)
  (org-narrow-to-subtree)
  (org-columns))

Stuck Projects

(setq org-stuck-projects '("/PROJECT" ("NEXT") nil ""))
(defun cpm/my-org-agenda-list-stuck-projects ()
  (interactive)
  (cpm/org-goto-projects)
  (org-agenda nil "#" 'subtree))

Areas

(defun cpm/go-to-areas ()
    (interactive)
    (find-file "~/Dropbox/org-files/todo.org")
    (widen)
    (beginning-of-buffer)
    (re-search-forward "* Areas")
    (beginning-of-line))

(defun cpm/areas-overview ()
    (interactive)
    (go-to-areas)
    (org-narrow-to-subtree)
    (org-columns))

Random Notes

(use-package org-randomnote
  :commands (org-randomnote org-randomnote--go-to-random-header org-randomnote--get-random-file org-randomnote--get-random-subtree)
  :init
  (setq org-randomnote-candidates '("~/Dropbox/org-files/todo.org")))

Org Functions

Some useful org-specific functions

Org-Fill Functions

Functions to calculate apt offsets and call regular org fill stuff. There’s a useful stack overflow thread on this.

(defun calc-offset-on-org-level ()
  "Calculate offset (in chars) on current level in org mode file."
  (* (or (org-current-level) 0) org-indent-indentation-per-level))

(defun my-org-fill-paragraph (&optional JUSTIFY)
  "Calculate apt fill-column value and fill paragraph."
  (let* ((fill-column (- fill-column (calc-offset-on-org-level))))
    (org-fill-paragraph JUSTIFY)))

(defun my-org-auto-fill-function ()
  "Calculate apt fill-column value and do auto-fill"
  (let* ((fill-column (- fill-column (calc-offset-on-org-level))))
    (org-auto-fill-function)))
  
(defun my-org-mode-hook ()
  (setq fill-paragraph-function   'my-org-fill-paragraph
        normal-auto-fill-function 'my-org-auto-fill-function))

;; (add-hook 'org-load-hook 'my-org-mode-hook)
;; (add-hook 'org-mode-hook 'my-org-mode-hook)

Narrow & Advance/Retreat

Functions to advance forwards or backwards through narrowed tree

(defun cpm/org-advance ()
  (interactive)
  (when (buffer-narrowed-p)
    (beginning-of-buffer)
    (widen)
    (org-forward-heading-same-level 1))
  (org-narrow-to-subtree))

(defun cpm/org-retreat ()
  (interactive)
  (when (buffer-narrowed-p)
    (beginning-of-buffer)
    (widen)
    (org-backward-heading-same-level 1))
  (org-narrow-to-subtree))

Org files

(defun cpm/goto-org-files ()
  "goto org-files directory"
  (interactive)
  (require 'ranger)
  (find-file "~/Dropbox/org-files"))
(defun cpm/goto-mail-inbox.org ()
  "goto org-mail-inbox"
  (interactive)
  (find-file "~/Dropbox/org-files/mail.org"))
(defun cpm/goto-todo.org ()
  "goto org-todo"
  (interactive)
  (find-file "~/Dropbox/org-files/todo.org"))
(defun cpm/goto-articles.org ()
  "goto org-articles"
  (interactive)
  (find-file "~/Dropbox/org-files/articles.org"))
(defun cpm/goto-classes.org ()
  "goto org-classes"
  (interactive)
  (find-file "~/Dropbox/org-files/classes.org"))
(defun cpm/goto-notes.org ()
  "goto org-notes"
  (interactive)
  (find-file "~/Dropbox/org-files/org-notes.org"))
(defun cpm/goto-someday.org ()
  "goto org-someday"
  (interactive)
  (find-file "~/Dropbox/org-files/someday.org"))

Export Headings as Separate PDF Files

Helpful advice from pragmatic emacs

;; export headlines to separate files
;; http://emacs.stackexchange.com/questions/2259/how-to-export-top-level-headings-of-org-mode-buffer-to-separate-files
(defun cpm/org-export-headlines-to-pdf ()
  "Export all subtrees that are *not* tagged with :noexport: to
separate files.

Subtrees that do not have the :EXPORT_FILE_NAME: property set
are exported to a filename derived from the headline text."
  (interactive)
  (save-buffer)
  (let ((modifiedp (buffer-modified-p)))
    (save-excursion
      (goto-char (point-min))
      (goto-char (re-search-forward "^*"))
      (set-mark (line-beginning-position))
      (goto-char (point-max))
      (org-map-entries
       (lambda ()
         (let ((export-file (org-entry-get (point) "EXPORT_FILE_NAME")))
           (unless export-file
             (org-set-property
              "EXPORT_FILE_NAME"
              (replace-regexp-in-string " " "_" (nth 4 (org-heading-components)))))
           (deactivate-mark)
           (org-pandoc-export-to-latex-pdf nil t)
           (unless export-file (org-delete-property "EXPORT_FILE_NAME"))
           (set-buffer-modified-p modifiedp)))
       "-noexport" 'region-start-level))))

Org demote/promote region

(defun endless/demote-everything (number beg end)
  "Add a NUMBER of * to all headlines between BEG and END.
Interactively, NUMBER is the prefix argument and BEG and END are
the region boundaries."
  (interactive "p\nr")
  (save-excursion
    (save-restriction
      (save-match-data
        (widen)
        (narrow-to-region beg end)
        (goto-char (point-min))
        (let ((string (make-string number ?*)))
          (while (search-forward-regexp "^\\*" nil t)
            (insert string)))))))

Org Rifle

Search rapidly through org files using helm

(use-package helm-org-rifle
  :commands (helm-org-rifle helm-org-rifle-agenda-files helm-org-rifle-org-directory))

Org-Download

Drag and drop images to Emacs org-mode. Courtesy of abo-abo.

(use-package org-download
  :commands (org-download-yank org-download-screenshot org-download-image)
  :config
   (setq org-download-method 'directory)
         org-download-image-dir "~/Dropbox/org-files/org-pictures"
         org-download-image-latex-width 500
 )

Org Pomodoro

Helps with time tracking

(use-package org-pomodoro
  :commands org-pomodoro
  :init
  (progn
    (setq org-pomodoro-audio-player "/usr/bin/afplay")))

Org Indirect Buffer

Some advice to automatically switch to a new indirect buffer upon creation

(defadvice org-tree-to-indirect-buffer (after org-tree-to-indirect-buffer-after activate) (other-window 1))

Org Numbers Overlay

This is a useful minor-mode to number org-mode headings. It came up in the course of this reddit discussion.

(define-minor-mode org-numbers-overlay-mode
  "Add overlays to org headings which number them"
  nil " *1." nil

  (let ((hooks '(after-save-hook
                 org-insert-heading-hook))
        (funcs '(org-promote
                 org-cycle-level
                 org-promote-subtree
                 org-demote
                 org-demote-subtree
                 org-move-subtree-up
                 org-move-subtree-down
                 org-move-item-down
                 org-move-item-up
                 org-cut-subtree
                 org-insert-todo-heading
                 org-insert-todo-subheading
                 org-meta-return
                 org-set-property
                 org-move)))
    (if org-numbers-overlay-mode
        (progn
          (org-numbers-overlay-update)
          (dolist (fn funcs)
            (advice-add fn :after #'org-numbers-overlay-update))
          (dolist (hook hooks)
            (add-hook hook #'org-numbers-overlay-update)))

      (progn
        (dolist (fn funcs)
          (advice-add fn :after #'org-numbers-overlay-update))
        (dolist (hook hooks)
          (remove-hook hook #'org-numbers-overlay-update))

        (loop for o in (overlays-in (point-min) (point-max))
              if (eq (overlay-get o 'type) 'org-number)
              do (delete-overlay o))))))

(defun org-numbers-overlay-update (&rest args)
  (when org-numbers-overlay-mode
    (let ((levels (make-vector 10 0)))
      (save-excursion
        (widen)
        (goto-char (point-min))
        (while (outline-next-heading)
          (if (assoc "UNNUMBERED" (org-entry-properties))
              ;; if it's unnumbered delete any overlays we have on it
              (loop for o in (overlays-in (point)
                                          (save-excursion (end-of-line) (point)))
                    if (eq (overlay-get o 'type) 'org-number)
                    do (delete-overlay o))
            ;; if it's not unnumbered add a number or update it
            (let* ((detail (org-heading-components))
                   (level (- (car detail) 1))
                   (lcounter (1+ (aref levels level)))
                   (o (or (loop for o in (overlays-in (point)
                                                      (save-excursion (end-of-line) (point)))
                                if (eq (overlay-get o 'type) 'org-number)
                                return o)
                          (make-overlay (point) (+ (point) (car detail))))))
              (aset levels level lcounter)
              (loop for i from (1+ level) to 9
                    do (aset levels i 0))
              (overlay-put o 'type 'org-number)
              (overlay-put o 'evaporate t)
              (overlay-put o 'after-string
                           (let (s)
                             (loop for i across levels
                                   until (zerop i)
                                   do (setf s (if s (format "%s.%d" s i)
                                                (format " %d" i))
                                            ))
                             s)))))))))
(provide 'org-numbers-overlay)

Org Export

Some useful settings

;; backends
(setq org-export-backends '(ascii html icalendar latex odt pandoc hugo md))

Org & Pandoc (Ox-pandoc)

(use-package ox-pandoc
  :after ox
  :config
  ;; default options for all output formats
  (setq org-pandoc-command (expand-file-name "/usr/local/bin/pandoc"))
  (setq org-pandoc-options '((standalone . t)))
  ;; cancel above settings only for 'docx' format
  (setq org-pandoc-options-for-docx '((standalone . nil)))
  ;; special settings for beamer-pdf and latex-pdf exporters
  (setq org-pandoc-options-for-beamer-pdf '((pdf-engine . "xelatex")))
  (setq org-pandoc-options-for-latex-pdf '((pdf-engine . "xelatex")))
  (setq org-pandoc-format-extensions '(org+smart)))

Ox-Hugo

Export to Hugo with Org

(use-package ox-hugo :after ox)
(use-package ox-hugo-auto-export :ensure nil :after ox-hugo)

Org Export Top-Level Trees

From a useful stack exchange post

(defun cpm/org-map-entries (org-file in-tags func)
  (let ((tags (if (stringp in-tags)
                   (list in-tags)
                 in-tags)))

    (with-temp-buffer
      (org-mode)
      (insert-file-contents org-file-main)

      ;; Execute func at each heading that matches tags.
      (while (< (point) (point-max))

        ;; If find a heading...
        (and (search-forward-regexp "^\* " nil "end")

             ;; ...that matches the given tags...
             (seq-reduce
              (lambda(a b) (and a b))
              (mapcar
               (lambda (tag)
                 (beginning-of-line)
                 (search-forward-regexp
                  (concat ":" tag ":") (line-end-position) "end"))
               tags)
              t)

             ;; ... then execute given function with cursor at beginning of
             ;; heading.
             (progn
               (beginning-of-line)
               (save-excursion
                 (funcall func))
               (end-of-line)))))))

Org Miscellaneous Packages

Other useful org packages

(use-package htmlize :commands (htmlize-buffer))
(use-package org-inlinetask :ensure nil :commands org-inlinetask-insert-task)
;; ignore export of headlines marked with :ignore: tag
(with-eval-after-load 'org
  (require 'ox-extra)
  (ox-extras-activate '(ignore-headlines)))

Org Keybindings

Evil Org

(use-package evil-org
  :ensure t
  :after org
  :config
  (add-hook 'org-mode-hook 'evil-org-mode)
  (add-hook 'evil-org-mode-hook
            (lambda ()
              (evil-org-set-key-theme '(textobjects insert navigation additional shift))))
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys))

Org Local Leader Keybindings

(general-define-key
:states '(normal visual)
:keymaps 'org-mode-map
:prefix "SPC m" 
:non-normal-prefix "C-SPC m"

 ""    '(nil :which-key "Local Leader")
 "RET" #'cpm/org-archive-done-tasks
 "SPC" #'org-toggle-checkbox
 "."   #'org-cycle-agenda-files
 "/"   #'org-sparse-tree
 "="   #'org-align-all-tags
 "?"   #'org-tags-view
 ":"   #'org-set-tags
 "a"   #'super-jump-to-org-agenda
 "A"   #'org-archive-subtree
 "b"   #'org-tree-to-indirect-buffer
 "B"   #'org-babel-tangle
 "c"   #'org-capture
 "d"   #'org-time-stamp
 "D"   #'org-deadline
 "e"   #'org-edit-special
 "n"   #'cpm/narrow-or-widen-dwim
 "r"   #'org-refile
 "s"   #'org-schedule
 "t"   #'counsel-org-tag
 "T"   #'org-todo
 "v"   #'variable-pitch-mode
 "l"   #'org-insert-link
 "L"   #'org-store-link
 "+"   #'org-timestamp-up-day
 "-"   #'org-timestamp-down-day
 "<"   #'org-metaleft
 ">"   #'org-metaright

 "i"  '(:ignore t :which-key "Insert...")
       "il" #'org-insert-link
       "if" #'org-footnote-new

 "R"  '(:ignore t :which-key "RevealJS..." )
       "Rr" #'cpm/reveal-to-html-open
       "Rs" #'cpm/narrowed-subtree-to-html
       "RS" #'org-reveal-export-current-subtree
       "Rp" #'cpm/reveal-to-pdf)

(general-define-key
  :states '(normal motion emacs)
  :keymaps 'org-agenda-mode-map
  :prefix "SPC"
  :non-normal-prefix "C-SPC"
    "<escape>" #'org-agenda-Quit
    "m"   #'org-agenda-month-view
    "C-j" #'org-agenda-next-item
    "C-k" #'org-agenda-previous-item
    "C-n" #'org-agenda-next-item
    "C-p" #'org-agenda-previous-item)

Alternate Bindings

(general-define-key :states '(normal) :keymaps 'org-mode-map
      "RET" 'org-open-at-point     ;; Open with return in evil
      "p"   'org-yank ;; better pasting behavior
      "s-J" 'crux-top-join-line)
;;   normal, insert, visual shortcuts
  (general-define-key :states '(normal insert visual) :keymaps 'org-mode-map
    "M-q" #'cpm/fill-or-unfill)
;;   ;; normal & insert state shortcuts.
  (general-define-key :states '(normal insert) :keymaps 'org-mode-map
     ;; easily emphasize text
     ;; see https://emacs.stackexchange.com/questions/27645/unable-to-bind-emphasize-key-in-org-mode
     "s-b" (lambda () (interactive) (org-emphasize ?\*))
     "s-i" (lambda () (interactive) (org-emphasize ?\/))
     "s-=" (lambda () (interactive) (org-emphasize ?\=))
      ;; better pasting behavior in org-mode
     "s-v" 'org-yank)

Research & Writing

Deft

(use-package deft
  :ensure t
  :commands (deft deft-open-file-other-window big-notes zd-new-file deft-new-file-named)
  :general
  (:keymaps 'deft-mode-map :states '(normal motion)
   "o" 'cpm/deft-open)
  (:keymaps 'deft-mode-map :states '(insert)
   "C-j" 'evil-next-line
   "C-k" 'evil-previous-line
   "C-o" 'cpm/deft-open)
  :config
  (add-to-list 'evil-insert-state-modes 'deft-mode)
  ;; basic settings for use with zettel
  (setq deft-directory (concat org-directory "/zettel/")
        deft-recursive t
        deft-extensions '("org" "md" "txt")
        deft-default-extension "org"
        deft-new-file-format zd-id-format
        deft-org-mode-title-prefix t)
  ;; file renaming rules
  (setq deft-file-naming-rules
      '((noslash . "-")
        (nospace . "-")
        (case-fn . downcase)))
  ;;function to run deft in specified directory
  (defun any-deft (dir)
    "Run deft in directory DIR"
    (setq deft-directory dir)
    (switch-to-buffer "*Deft*")
    (kill-this-buffer)
    (require 'org)
    (deft))
  (defun big-notes ()
    "Goto main notes with deft"
    (interactive)
    (any-deft "~/Dropbox/Notes")
    (kill-this-buffer)
    (any-deft "~/Dropbox/Notes"))
  (defun cpm/deft-open ()
    (interactive)
    (deft-open-file-other-window t)))

Zettelkasten

A useful set of functions for managing a Zettelkasten. From EFLS.

(setq deft-strip-summary-regexp
 (concat "\\("
         "[\n\t]" ;; blank
         "\\|^#\\+[a-zA-Z_]+:.*$" ;;org-mode metadata
         "\\)"))

(defun zd-get-thing-at-point ()
"Return the thing at point, which can be a link, tag or word."
  (require 'thingatpt)
  (let* ((link-re "\\[\\[\\([^]]+\\)\\]\\]")
         (htag-re "\\([§#@][[:alnum:]_-]+\\)"))
   (cond
    ((thing-at-point-looking-at link-re)
      (match-string-no-properties 1))
     ((thing-at-point-looking-at htag-re)
      (match-string-no-properties 1))
     (t (thing-at-point 'word t)))
  ))

(defun zd-search-at-point ()
"Search deft with thing-at-point as filter.
Thing can be a double-bracketed link, a hashtag, or a word."
  (interactive)
  (let ((string (zd-get-thing-at-point)))
   (if string
       (zd-search-global string t)
     (user-error "No search term at point")))
  )

(defun zd-search-global (str &optional dntOpn)
"Search deft with STR as filter.
If there is only one result, open that file (unless DNTOPN is true)."
  ;; Sanitize the filter string
  (setq str (replace-regexp-in-string "[[:space:]\n]+" " " str))
  ;; Call deft search on the filter string
  (let ((deft-incremental-search t))
   (deft)
   (deft-filter str t))
  ;; If there is a single match, open the file
  (unless dntOpn
   (when (eq (length deft-current-files) 1)
     (deft-open-file (car deft-current-files)))))

(defun zd-search-filename (str)
"Search for deft files with string STR in filename.
Open if there is only one result."
  ;; Sanitize the filter string
  (setq str (replace-regexp-in-string "[[:space:]\n]+" " " str))
  ;; Call deft search on the filter string
  (let ((deft-filter-only-filenames t))
   (deft)
   (deft-filter str t))
  ;; If there is a single match, open the file
  (when (eq (length deft-current-files) 1)
    (deft-open-file (car deft-current-files))))

(defun zd-search-current-id ()
"Search deft with the id of the current file as filter.
Open if there is only one result."
 (interactive)
 (zd-search-global (zd-id-current-file) t)
)

(defcustom zd-id-format "%Y-%m-%d-%H%M"
  "Format used when generating zetteldeft IDs."
  :type 'string
  :group 'zetteldeft
)

(setq deft-new-file-format zd-id-format)

(defun zd-generate-id ()
 "Generates an id in `zd-id-format'."
 (format-time-string zd-id-format)
)

(defun zd-id-insert ()
 (interactive)
 "Inserts an id in `zd-id-format'."
 (insert (zd-generate-id) " ")
)

(defun zd-id-sanitized (str)
"Strip STRING from everything that is not a number or a dash."
 (replace-regexp-in-string "[^(0-9)-]+" "" str)
)

(defun zd-file-id-stripped (file)
"Returns file id stripped from given filename FILE."
 (let ((file (substring file 0 16)))
   (zd-id-sanitized file)
))

(defun zd-id-current-file ()
"Return the id from the filename the buffer is currently visiting."
 (zd-file-id-stripped (file-name-base (buffer-file-name)))
)

(defun zd-copy-id-current-file ()
"Add the id from the filename the buffer is currently visiting to the kill ring."
(interactive)
 (kill-new (zd-id-current-file))
)

(defun zd-find-file (file)
"Open deft file FILE."
 (interactive
  (list (completing-read "Deft find file: "
        (deft-find-all-files-no-prefix))))
 (deft-find-file file)
)

(defun zd-find-file-id-copy (file)
"Find deft file FILE and add its id to the kill ring."
 (interactive (list
        (completing-read "File to copy id from: "
        (deft-find-all-files-no-prefix))))
  (kill-new (concat "§" (zd-file-id-stripped file)))
)

(defun zd-find-file-id-insert (file)
"Find deft file FILE and insert its link id, prepended by §."
 (interactive (list
        (completing-read "File to insert id from: "
        (deft-find-all-files-no-prefix))))
  (insert (concat "§" (zd-file-id-stripped file)))
)

(defun zd-find-file-full-title-insert (file)
"Find deft file FILE and insert its link id with title, prepended by §."
 (interactive (list
        (completing-read "File to insert full title from: "
        (deft-find-all-files-no-prefix))))
  (insert (concat "§" (file-name-base file)))
)

(defun zd-new-file (str &optional empty)
"Create a new deft file. Filename is `zd-id-format' appended by STR. No extension needed.

After creating, the title is inserted in org-mode format (unless EMPTY is true) and the full file name is added to the kill ring."
 (interactive (list (read-string "name: ")))
 (let* ((zdId (zd-generate-id))
        (zdName (concat zdId " " str)))
 (deft-new-file-named zdName)
 (kill-new zdName)
 (unless empty (zd-insert-org-title))
 (when (featurep 'evil) (evil-insert-state))
))

(defun zd-new-file-and-link (str)
"Inserts generated id with `zd-id-format' appended with STR.
Creates new deft file with id and STR as name."
 (interactive (list (read-string "name: ")))
 (insert "§" (zd-generate-id) " " str)
 (zd-new-file str)
)

(defun zd-avy-tag-search ()
"Call on avy to jump and search tags indicated with #."
 (interactive)
 (save-excursion
  (avy-goto-char ?#)
  (zd-search-at-point)
))

(defun zd-avy-link-search ()
"Call on avy to jump and search link ids indicated with §.
Opens immediately if there is only one result."
 (interactive)
 (save-excursion
  (avy-goto-char )
  (zd-search-global (zd-id-sanitized (zd-get-thing-at-point)))
))

(defun zd-avy-file-search ()
"Call on avy to jump to link ids indicated with § and use it to search for filenames."
 (interactive)
 (save-excursion
  (avy-goto-char )
  (zd-search-filename (zd-id-sanitized (zd-get-thing-at-point)))
))

(defun zd-deft-new-search ()
"Launch deft, clear filter and enter insert state."
 (interactive)
 (deft)
 (deft-filter-clear)
 (any-deft "~/Dropbox/org-files/zettel/")
 (when (featurep 'evil) (evil-insert-state))
)

(defun zd-file-rename ()
"Rename the current file via the deft function. Use this on files in the deft-directory."
 (interactive)
  (let ((old-filename (buffer-file-name))
        (deft-dir (file-name-as-directory deft-directory))
        new-filename old-name new-name)
    (when old-filename
      (setq old-name (deft-base-filename old-filename))
      (setq new-name (read-string
                      (concat "Rename " old-name " to (without extension): ")
                      old-name))
      (setq new-filename
            (concat deft-dir new-name "." deft-default-extension))
      (rename-file old-filename new-filename)
      (deft-update-visiting-buffers old-filename new-filename)
      (deft-refresh))))

(defun zd-insert-org-title ()
 (interactive)
 (insert
   "#+title: "
   (zd-lift-file-title (file-name-base (buffer-file-name)))
   "\n"
   zd-string-below-title
   ))

(defcustom zd-string-below-title ""
  "String inserted below title when `zd-insert-org-title' is called. Empty by default."
  :type 'string
  :group 'zetteldeft
)

(defun zd-get-file-list (srch)
"Returns a list of files with the search item SRCH."
  (let ((deft-current-sort-method 'title))
   (deft-filter srch t)
   deft-current-files
))

(defun zd-lift-file-title (zdFile)
 (let ((baseName (file-name-base zdFile)))
   (replace-regexp-in-string
    "[0-9]\\{2,\\}-[0-9-]+[[:space:]]"
    "" baseName)
))

(defun zd-insert-list-links (zdSrch)
"Inserts at point a list of links to all deft files with a search string ZDSRCH.
When searching for a tag, include # manually in the prompt."
 (interactive (list (read-string "search string: ")))
 (dolist (zdFile (zd-get-file-list zdSrch))
  (zd-list-entry-file-link zdFile)
))

(defun zd-list-entry-file-link (zdFile)
"Insert ZDFILE as list entry."
 (insert " - " (concat "§" (file-name-base zdFile)) "\n")
)

(defun zd-org-include-search (zdSrch)
"Inserts at point org-mode code to include all files with the selected tag. Include the # manually in the prompt."
 (interactive (list (read-string "tag (include the #): ")))
 (dolist (zdFile (zd-get-file-list zdSrch))
  (zd-org-include-file zdFile)
))

(defun zd-org-include-file (zdFile)
"Insert code to include org-file zdFile."
 (insert
   ;; Insert org-mode title
   "\n* " (zd-lift-file-title zdFile) "\n"
   ;; Insert #+INCLUDE: "file.org" :lines 2-
   "#+INCLUDE: \"" zdFile "\" :lines \"2-\"\n"
 ))

;; ; highlight zetteldeft links
;; (font-lock-add-keywords 'org-mode '(
;;   ("§[0-9]\\{2,\\}-[0-9-]+" . font-lock-warning-face)
;;   ))

;; (with-eval-after-load 'deft
;;   (define-key spacemacs-deft-mode-map-prefix
;;     "o" 'efls/deft-open)
;;   (define-key spacemacs-deft-mode-map-prefix
;;     [?\t] 'efls/deft-open-preview)
;;  )

(defun efls/deft-open-other ()
 (interactive)
 (deft-open-file-other-window t)
)

(defun efls/deft-open-preview ()
 (interactive)
 (deft-open-file-other-window)
)

(with-eval-after-load 'deft
  (define-key deft-mode-map
    (kbd "<tab>") 'efls/deft-open-preview)
  (define-key deft-mode-map
    (kbd "<s-return>") 'efls/deft-open-other)
)

Spelling

Use flyspell and aspell

(use-package ispell
  :commands (ispell-word ispell-region ispell-buffer)
  :config
  (when (executable-find "aspell")
    (setq ispell-program-name "aspell")
    ;; Please note ispell-extra-args contains ACTUAL parameters passed to aspell
    (setq ispell-extra-args '("--sug-mode=ultra" "--lang=en_US"))))
  ;; (when (executable-find "hunspell")
  ;;   (setq-default ispell-program-name "hunspell")
  ;;   (setq ispell-extra-args   '("-d en_US"))
  ;;   (setq ispell-really-hunspell t)))

  ;; Save a new word to personal dictionary without asking
  ;; (setq ispell-silently-savep nil)

  ;; (setq-default ispell-program-name "/usr/local/bin/aspell")
  ;; (setq ispell-extra-args
  ;;     (list "--sug-mode=fast" ;; ultra|fast|normal|bad-spellers
  ;;           "--lang=en_US"
  ;;           "--ignore=3")))

(use-package flyspell
  :commands (flyspell-auto-correct-previous-word flyspell-correct-word-generic)
  :init
  ;; Below variables need to be set before `flyspell' is loaded.
  (setq flyspell-use-meta-tab nil)
  :custom
  (flyspell-abbrev-p t)
  (flyspell-use-global-abbrev-table-p t)
  (flyspell-issue-message-flag nil)
  (flyspell-issue-welcome-flag nil)
  :config
  ;; Use mouse
  (define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word)
  (define-key flyspell-mouse-map [mouse-3] #'undefined))
  (add-hook 'flyspell-mode-hook 'flyspell-buffer) ; show misspelled

(use-package flyspell-correct-ivy
  :general
  (:states '(normal insert emacs) :keymaps 'flyspell-mode-map
   "C-;" 'flyspell-auto-correct-previous-word
   "C-:" 'flyspell-correct-word-generic)
  :custom (flyspell-correct-interface 'flyspell-correct-ivy))

(with-eval-after-load 'hydra
  (defhydra hydra-spelling (:color blue)
  "
  ^
  ^Spelling^          ^Errors^            ^Checker^
  ^────────^──────────^──────^────────────^───────^───────
  _q_ quit            _<_ previous        _c_ correction
  ^^                  _>_ next            _d_ dictionary
  ^^                  _f_ check           _m_ mode
  ^^                  ^^                  ^^
  "
  ("q" nil)
  ("<" flyspell-correct-previous :color pink)
  (">" flyspell-correct-next :color pink)
  ("c" ispell)
  ("d" ispell-change-dictionary)
  ("f" flyspell-buffer :color pink)
  ("m" flyspell-mode)))

Abbrev

Abbrev mode

(use-package abbrev
  :ensure nil
  :defer 2
  :config
  (add-hook 'text-mode-hook #'abbrev-mode)
  (setq abbrev-file-name (concat cpm-local-dir "abbrev/.abbrev_defs")
        save-abbrevs 'silently)
  (if (file-exists-p abbrev-file-name)
      (quietly-read-abbrev-file)))

Helm-Bibtex

Great for managing citations and notes

(use-package helm-bibtex
  :commands helm-bibtex
  :config
  ;; Set insert citekey with markdown citekeys for org-mode
  (setq bibtex-completion-format-citation-functions
        '((org-mode    . bibtex-completion-format-citation-pandoc-citeproc)
        (latex-mode    . bibtex-completion-format-citation-cite)
        (markdown-mode . bibtex-completion-format-citation-pandoc-citeproc)
        (default       . bibtex-completion-format-citation-default)))
  (setq bibtex-completion-display-formats
        '((t . "${author:36} ${title:*} ${year:4} ${=has-pdf=:1}${=has-note=:1} ${=type=:7}")))
  ;; Set default action for helm-bibtex as inserting citation
  (helm-delete-action-from-source "Insert citation" helm-source-bibtex)
  (helm-add-action-to-source "Insert citation" 'helm-bibtex-insert-citation helm-source-bibtex 0)
  (setq bibtex-completion-pdf-symbol "")
  (setq bibtex-completion-notes-symbol "")
  (setq bibtex-completion-notes-template-one-file
 "* ${author} (${date}): ${title} 
:PROPERTIES:
:INTERLEAVE_PDF: ${file}
:Custom_ID: ${=key=}
:END: 
[[pdfview:${file}][file link]]
     ")
  (setq bibtex-completion-bibliography "~/Dropbox/Work/All.bib" 
        bibtex-completion-library-path "~/Dropbox/Work/be-master-pdflib/"
        bibtex-completion-pdf-field nil
        bibtex-completion-notes-path "~/Dropbox/org-files/org-brain/references/"
        ;; bibtex-completion-additional-search-fields '(keywords)
        bibtex-completion-notes-extension ".org"
        helm-bibtex-full-frame nil) 

;; Set global shortcut for calling helm-bibtex
)

Markdown mode

Markdown settings

(use-package markdown-mode
  :mode (("\\.markdown\\'" . markdown-mode)
         ("\\.md\\'"       . markdown-mode))
  :init
  ;; markdown hooks
  (add-hook 'markdown-mode-hook
        '(lambda ()
        (turn-on-flyspell) (centered-cursor-mode) (git-gutter-mode 1) (set-fill-column 78) (hl-todo-mode)))
  (setq markdown-command "pandoc -s -N --smart --bibliography=~/Dropbox/Work/Master.bib"
          markdown-enable-math t
          markdown-nested-imenu-heading-index t
          markdown-open-command "~/bin/scripts/mark.sh"
          markdown-footnote-location 'immediately
          )
     ;; add keybindings to hook
   :config
   ;; remove strikout comment face
   (set-face-attribute 'markdown-comment-face nil 
   :weight 'bold :strike-through nil)

   )

Pandoc

Pandoc mode for markdown conversion

(use-package pandoc-mode
  :commands (cpm/pandoc-convert-to-pdf run-pandoc pandoc-convert-to-pdf)
  :config
  (progn
    (defun run-pandoc ()
      "Start pandoc for the buffer and open the menu"
      (interactive)
      (pandoc-mode)
      (pandoc-main-hydra/body))
    (add-hook 'pandoc-mode-hook 'pandoc-load-default-settings)

  (defun cpm/pandoc-convert-to-pdf ()
   (interactive)
   (cond
   ((eq major-mode 'org-mode)
    (call-interactively 'org-pandoc-export-to-latex-pdf-and-open))
   (t
    (call-interactively 'pandoc-convert-to-pdf) (cpm/pandoc-pdf-open) (evil-window-prev 1))))

  (defun cpm/pandoc-command-line-convert-to-pdf ()
   "convert to pdf"
   (interactive)
   (evil-ex "!pandoc -s -N -V mainfont=Optima --pdf-engine=xelatex --bibliography=~/Dropbox/Work/Master.bib --template=~/.pandoc/pandoc-templates/default.latex -o '%.pdf' '%'"))

  (defun cpm/pandoc-pdf-open ()
   "Open created PDF file"  
   (interactive)
   (find-file-other-window (concat (file-name-sans-extension buffer-file-name) ".pdf"))))
  :init
  (progn
    (setq pandoc-data-dir (concat cpm-etc-dir "pandoc-mode/"))
    ;; help pandoc find xelatex
    (setenv "PATH" (concat (getenv "PATH") ":/Library/TeX/texbin"))))

Writeroom (Distraction free mode)

This simulates programs like writeroom that provide a distraction-free mode of writing.

(use-package writeroom-mode
  :commands (writeroom-mode)
  :config
  (setq writeroom-fullscreen-effect 'maximized)
  (setq writeroom-width 85))
    (defun distraction-free ()
    "distraction free writing"
      (interactive)
      (git-gutter-mode 0) 
      (linum-mode 0) 
      (centered-cursor-mode)
      (writeroom-mode)
      )

Interleave

Interleave your reading notes with the PDF.

(use-package interleave
  :commands interleave)

Lorem ipsum

Make arbitrary blocks or sentences of text.

(use-package lorem-ipsum
  :commands (Lorem-ipsum-insert-sentences Lorem-ipsum-insert-list Lorem-ipsum-insert-paragraphs)
  :config 
  (lorem-ipsum-use-default-bindings)
  )

Palimpsest mode

(use-package palimpsest
  :defer t
  :diminish palimpsest-mode
  :init 
  (add-hook 'markdown-mode-hook 'palimpsest-mode)
  (add-hook 'org-mode-hook 'palimpsest-mode)
  :config
  (setq palimpsest-trash-file-suffix ".archive"))

LaTeX

;; Basic settings
(use-package auctex
  :mode ("\\.tex\\'" . latex-mode)
  :commands (latex-mode LaTeX-mode plain-tex-mode)
  :init
  (progn
    (add-hook 'LaTeX-mode-hook #'LaTeX-preview-setup)
    (add-hook 'LaTeX-mode-hook #'flyspell-mode)
    (add-hook 'LaTeX-mode-hook #'turn-on-reftex)
    (setq-default TeX-engine 'xetex)
    (setq TeX-auto-save t
          TeX-parse-self t
          TeX-save-query nil
          TeX-PDF-mode t)
    (setq-default TeX-master nil)))

(use-package preview
  :ensure nil
  :after auctex
  :commands LaTeX-preview-setup
  :init
  (progn
    (setq-default preview-scale 1.4
      preview-scale-function '(lambda () (* (/ 10.0 (preview-document-pt)) preview-scale)))))

(use-package reftex
  :commands turn-on-reftex
  :init
  (progn
    (setq reftex-plug-into-AUCTeX t)))

(use-package bibtex
  :defer t
  :mode ("\\.bib" . bibtex-mode)
  :init
  (progn
    (setq bibtex-align-at-equal-sign t)
    (add-hook 'bibtex-mode-hook (lambda () (set-fill-column 120)))))


;; Auto-fill for LaTeX
(defun schnouki/latex-auto-fill ()
  "Turn on auto-fill for LaTeX mode."
  (turn-on-auto-fill)
  (set-fill-column 80)
  (setq default-justification 'left))
(add-hook 'LaTeX-mode-hook #'schnouki/latex-auto-fill)

;; Compilation command
(add-hook 'LaTeX-mode-hook (lambda () (setq compile-command "latexmk -pdflatex=xelatex -f -pdf %f")))

;; Prevent ispell from verifying some LaTeX commands
;; http://stat.genopole.cnrs.fr/dw/~jchiquet/fr/latex/emacslatex
(defvar schnouki/ispell-tex-skip-alists
      '("cite" "nocite"
  "includegraphics"
  "author" "affil"
  "ref" "eqref" "pageref"
  "label"))
(setq ispell-tex-skip-alists
      (list
       (append (car ispell-tex-skip-alists)
         (mapcar #'(lambda (cmd) (list (concat "\\\\" cmd) 'ispell-tex-arg-end)) schnouki/ispell-tex-skip-alists))
       (cadr ispell-tex-skip-alists)))

;; Indentation with align-current in LaTeX environments
(defvar schnouki/LaTeX-align-environments '("tabular" "tabular*"))
(add-hook 'LaTeX-mode-hook
    (lambda ()
      (require 'align)
      (setq LaTeX-indent-environment-list
      ;; For each item in the list...
      (mapcar (lambda (item)
          ;; The car is an environment
          (let ((env (car item)))
            ;; If this environment is in our list...
            (if (member env schnouki/LaTeX-align-environments)
          ;; ...then replace this item with a correct one
          (list env 'align-current)
        ;; else leave it alone
        item)))
        LaTeX-indent-environment-list))))

;; Use dvipdfmx to convert DVI files to PDF in AUCTeX
(eval-after-load 'tex
  '(add-to-list 'TeX-command-list
                '("DVI to PDF" "dvipdfmx %d" TeX-run-command t t) t))

;; SyncTeX (http://www.emacswiki.org/emacs/AUCTeX#toc19)
(defun synctex/un-urlify (fname-or-url)
  "A trivial function that replaces a prefix of file:/// with just /."
  (if (string= (substring fname-or-url 0 8) "file:///")
      (substring fname-or-url 7)
    fname-or-url))

PDF-Tools

Better than doc-view, but doesn’t render well on retina screens :(

  (use-package pdf-tools
    :mode (("\\.pdf$" . pdf-view-mode))
    :commands (pdf-view-mode)
    :config
    (progn
      (pdf-tools-install)
      (evil-set-initial-state 'pdf-view-mode 'normal)
      (evil-set-initial-state 'pdf-outline-buffer-mode 'normal)
      (general-define-key :states '(normal) :keymaps 'pdf-view-mode-map
          ;; Navigation
          "j"  'pdf-view-next-line-or-next-page
          "k"  'pdf-view-previous-line-or-previous-page
          "l"  'pdf-view-next-page 
          "h"  'pdf-view-previous-page
          "J"  'image-forward-hscroll
          "K"  'image-backward-hscroll
          "gg"  'pdf-view-first-page
          "G"  'pdf-view-last-page
          "gt"  'pdf-view-goto-page
          "gl"  'pdf-view-goto-label
          "u" 'pdf-view-scroll-down-or-previous-page
          "d" 'pdf-view-scroll-up-or-next-page
          "-"  'pdf-view-shrink
          "+"  'pdf-view-enlarge
          "="  'pdf-view-fit-page-to-window
          (kbd "C-u") 'pdf-view-scroll-down-or-previous-page
          (kbd "C-d") 'pdf-view-scroll-up-or-next-page
          (kbd "``")  'pdf-history-backward
          ;; Search
          "/" 'isearch-forward
          "?" 'isearch-backward
          ;; Actions
          "r"   'pdf-view-revert-buffer
          "o"   'pdf-links-action-perform
          "O"   'pdf-outline
          "!"   'bms/pdf-no-filter
          "#"   'bms/pdf-midnight-original
          )
      (general-define-key :states '(insert) :keymaps 'pdf-view-mode-map
          "y" 'pdf-view-kill-ring-save )

     ;; midnite mode
     (setq pdf-view-midnight-colors '("#839496" . "#002b36" )) ; original values

     (defun bms/pdf-no-filter ()
     "View pdf without colour filter."
     (interactive)
     (pdf-view-midnight-minor-mode -1)
     )

    ;; change midnite mode colours functions
    (defun bms/pdf-midnite-original ()
      "Set pdf-view-midnight-colors to original colours."
      (interactive)
      (setq pdf-view-midnight-colors '("#839496" . "#002b36" )) ; original values
      (pdf-view-midnight-minor-mode)
      )

    (defun bms/pdf-midnite-amber ()
      "Set pdf-view-midnight-colors to amber on dark slate blue."
      (interactive)
      (setq pdf-view-midnight-colors '("#ff9900" . "#0a0a12" )) ; amber
      (pdf-view-midnight-minor-mode)
      )

    (defun bms/pdf-midnite-green ()
      "Set pdf-view-midnight-colors to green on black."
      (interactive)
      (setq pdf-view-midnight-colors '("#00B800" . "#000000" )) ; green
      (pdf-view-midnight-minor-mode)
      )

    (defun bms/pdf-midnite-colour-schemes ()
      "Midnight mode colour schemes bound to keys"
            (local-set-key (kbd "!") (quote bms/pdf-no-filter))
            (local-set-key (kbd "@") (quote bms/pdf-midnite-amber))
            (local-set-key (kbd "#") (quote bms/pdf-midnite-green))
                (local-set-key (kbd "$") (quote bms/pdf-midnite-original))
     )

    (defun cpm/pdf-color-theme ()
      (if (eq active-theme 'solarized-light)
          (bms/pdf-no-filter)
        (bms/pdf-midnite-original)))

      ;; midnite mode hook
      (add-hook 'pdf-view-mode-hook (lambda ()
                                      ; automatically turns on midnight-mode for pdfs
                                      (pdf-view-midnight-minor-mode)
                                      (cpm/pdf-color-theme)
                                      (bms/pdf-midnite-colour-schemes)
                                      ; fixes blinking pdf in evil
                                      (blink-cursor-mode -1)
                                      (beacon-mode -1)))

))

Org PDF View

For annotation and jumping to file

(use-package org-pdfview
  :commands (org-pdfview-open)
  :after pdf-tools
  :init
  (add-to-list 'org-file-apps 
             '("\\.pdf\\'" . (lambda (file link)
                                (org-pdfview-open link))))
 )

Extract annotations

(use-package pdf-tools-org 
  :ensure nil
  :commands (pdf-tools-org-export-to-org pdf-tools-org-import-from-org))

    ;; Extracting annotations using pdf-tools
    ;; modified from https://github.com/politza/pdf-tools/pull/133 
    ;; taken from http://matt.hackinghistory.ca/2015/11/11/note-taking-with-pdf-tools/

    (defun mwp/pdf-multi-extract (sources)
    "Helper function to print highlighted text from a list of pdf's, with one org header per pdf, 
    and links back to page of highlight."
    (let (
          (output ""))
      (dolist (thispdf sources)
        (setq output (concat output (pdf-annot-markups-as-org-text thispdf nil level ))))
      (princ output))
    )

    (defun cpm/pdf-summary-extract (sources)
    "Helper function to print underlined text from a list of pdf's, with one org header per pdf, 
    and links back to page of highlight."
    (let (
          (output ""))
      (dolist (thispdf sources)
        (setq output (concat output (pdf-annot-summary-as-org-text thispdf nil level ))))
      (princ output))
    )

    ;; this is stolen from https://github.com/pinguim06/pdf-tools/commit/22629c746878f4e554d4e530306f3433d594a654
    (defun pdf-annot-edges-to-region (edges)
    "Attempt to get 4-entry region \(LEFT TOP RIGHT BOTTOM\) from several edges.
    We need this to import annotations and to get marked-up text, because annotations
    are referenced by its edges, but functions for these tasks need region."

    (let ((left0 (nth 0 (car edges)))
          (top0 (nth 1 (car edges)))
          (bottom0 (nth 3 (car edges)))
          (top1 (nth 1 (car (last edges))))
          (right1 (nth 2 (car (last edges))))
          (bottom1 (nth 3 (car (last edges))))
          (n (safe-length edges)))
      ;; we try to guess the line height to move
      ;; the region away from the boundary and
      ;; avoid double lines
      (list left0
            (+ top0 (/ (- bottom0 top0) 2))
            right1
            (- bottom1 (/ (- bottom1 top1) 2 )))))

    (defun pdf-annot-markups-as-org-text (pdfpath &optional title level)
    "Acquire highligh annotations as text, and return as org-heading"

    (interactive "fPath to PDF: ")  
    (let* ((outputstring "") ;; the text to be returned
            (title (or title (replace-regexp-in-string "-" " " (file-name-base pdfpath ))))
            (level (or level (1+ (org-current-level)))) ;; I guess if we're not in an org-buffer this will fail
            (levelstring (make-string level ?*)) ;; set headline to proper level
            (annots (sort (pdf-info-getannots nil pdfpath)  ;; get and sort all annots
                          'pdf-annot-compare-annotations)))
      ;; create the header
      (setq outputstring (concat levelstring " Quotes From " title "\n\n")) ;; create heading

      ;; extract text
      (mapc
        (lambda (annot) ;; traverse all annotations
          (if (eq 'highlight (assoc-default 'type annot))
              (let* ((page (assoc-default 'page annot))
                    ;; use pdf-annot-edges-to-region to get correct boundaries of annotation
                    (real-edges (pdf-annot-edges-to-region
                                  (pdf-annot-get annot 'markup-edges)))
                    (text (or (assoc-default 'subject annot) (assoc-default 'content annot)
                              (replace-regexp-in-string "\n" " " (pdf-info-gettext page real-edges nil pdfpath))))

                    (height (nth 1 real-edges)) ;; distance down the page
                    ;; use pdfview link directly to page number
                    (linktext (concat "[[pdfview:" pdfpath "::" (number-to-string page) 
                                      "++" (number-to-string height) "][" title  "]]" )))
                (setq outputstring (concat outputstring text " ("
                                          linktext ", " (number-to-string page) ")\n\n"))
                ))

          (if (eq 'text (assoc-default 'type annot))
              (let* ((page (assoc-default 'page annot))
                    ;; use pdf-annot-edges-to-region to get correct boundaries of annotation
                    (real-edges (pdf-annot-edges-to-region
                                  (pdf-annot-get annot 'markup-edges)))
                    (text (or (assoc-default 'subject annot) (assoc-default 'content annot)
                              (replace-regexp-in-string "\n" " " (pdf-info-gettext page real-edges nil pdfpath))))

                    (height (nth 1 real-edges)) ;; distance down the page
                    ;; use pdfview link directly to page number
                    (linktext (concat "[[pdfview:" pdfpath "::" (number-to-string page) 
                                      "++" (number-to-string height) "][" title  "]]" )))
                (setq outputstring (concat outputstring text " ("
                                          linktext ", " (number-to-string page) ")\n\n"))
                ))

            (if (eq 'underline (assoc-default 'type annot))
                (let* ((page (assoc-default 'page annot))
                      ;; use pdf-annot-edges-to-region to get correct boundaries of highlight
                      (real-edges (pdf-annot-edges-to-region
                                    (pdf-annot-get annot 'markup-edges)))
                      (text (or (assoc-default 'subject annot) (assoc-default 'content annot)
                                (replace-regexp-in-string "\n" " " (pdf-info-gettext page real-edges nil pdfpath))))

                      (height (nth 1 real-edges)) ;; distance down the page
                      ;; use pdfview link directly to page number
                      (linktext (concat "[[pdfview:" pdfpath "::" (number-to-string page) 
                                        "++" (number-to-string height) "][" title  "]]" )))
                  (setq outputstring (concat outputstring text " ("
                                            linktext ", " (number-to-string page) ")\n\n"))
                  ))
                )
        annots)
      outputstring ;; return the header
      )
    )

    (defun pdf-annot-summary-as-org-text (pdfpath &optional title level)
    "Acquire underlined annotations as text, and return as org-heading"

    (interactive "fPath to PDF: ")  
    (let* ((outputstring "") ;; the text to be returned
            (title (or title (replace-regexp-in-string "-" " " (file-name-base pdfpath ))))
            (level (or level (1+ (org-current-level)))) ;; I guess if we're not in an org-buffer this will fail
            (levelstring (make-string level ?*)) ;; set headline to proper level
            (annots (sort (pdf-info-getannots nil pdfpath)  ;; get and sort all annots
                          'pdf-annot-compare-annotations)))
      ;; create the header
      (setq outputstring (concat levelstring " Summary from " title "\n\n")) ;; create heading

      ;; extract text
      (mapc
        (lambda (annot) ;; traverse all annotations
            (if (eq 'underline (assoc-default 'type annot))
                (let* ((page (assoc-default 'page annot))
                      ;; use pdf-annot-edges-to-region to get correct boundaries of annotation
                      (real-edges (pdf-annot-edges-to-region
                                    (pdf-annot-get annot 'markup-edges)))
                      (text (or (assoc-default 'subject annot) (assoc-default 'content annot)
                                (replace-regexp-in-string "\n" " " (pdf-info-gettext page real-edges nil pdfpath))))

                      (height (nth 1 real-edges)) ;; distance down the page
                      ;; use pdfview link directly to page number
                      (linktext (concat "[[pdfview:" pdfpath "::" (number-to-string page) 
                                        "++" (number-to-string height) "][" title  "]]" )))
                  (setq outputstring (concat outputstring text " ("
                                            linktext ", " (number-to-string page) ")\n\n"))
                  ))
                )
        annots)
      outputstring ;; return the header
      )
    )

Remember

A built-in remember buffer

(use-package remember
 :ensure nil
 :commands (remember remember-notes)
 :config
 (setq remember-data-dir (concat cpm-cache-dir "remember")
       remember-data-file (concat cpm-cache-dir "remember/notes"))
 (unless (file-directory-p remember-data-dir)
         (make-directory remember-data-dir t)))
 

Miscellaneous Packages

Restart emacs

(use-package restart-emacs
  :commands restart-emacs)

Autorevert

Auto-revert buffers of changed files

(use-package autorevert                 
  :ensure nil
  :defer 2
  :init
  (setq auto-revert-interval 1)
  (global-auto-revert-mode 1)
  :config
  (progn 
    (setq auto-revert-verbose nil ; Shut up, please!
          revert-without-query '(".*") ;; disable revert query
          ;; Revert Dired buffers, too
          global-auto-revert-non-file-buffers t)))

URL & Request

(use-package request
  :defer t
  :ensure nil
  :config
  (setq request-storage-directory (concat cpm-cache-dir "request"))
)

Private

(let ((private (expand-file-name "private.el" cpm-cache-dir))) 
  (if (file-exists-p private)
	  (load-file private)))

Post-Initialization

Server

Start server. For the proper way to check whether a server is running see this post.

(use-package server
 :defer 5
 :config
  (if (display-graphic-p)
      (unless (and (boundp server-process) server-process)
         (message "Starting server...")
         (server-start)))
  )

Page Breaks

Dashboard depends on this.

(use-package page-break-lines
  :defer t
  :diminish "")

Dashboard

Startup with a dashboard listing recent files, bookmarks, and projects.

  (use-package dashboard
    :commands (dashboard-insert-startupify-lists cpm/dashboard)
    :config
    (setq dashboard-items '((agenda . 5)
                            (recents  . 5)
                            (bookmarks . 5)
                            (projects . 5)
                            (totd . 1)))

      ;; tip of the day
      (defun totd()
      (let* ((commands (loop for s being the symbols
      when (commandp s) collect s))
      (command (nth (random (length commands)) commands)))
      (insert
      (format "** Tip of the day: ** \nCommand: %s\n\n%s\n\nInvoke with:\n\n"
      (symbol-value 'command)
      (documentation command)))
      (where-is command t)))
      (defun dashboard-insert-totd (list-size) (totd)) (add-to-list 'dashboard-item-generators '(totd . dashboard-insert-totd))

      (setq dashboard-startup-banner 2)
      ;; (dashboard-setup-startup-hook)
      (map! (:map dashboard-mode-map
        :ni     "TAB" 'widget-forward
        :ni     "C-i" 'widget-forward
        :ni     "backtab" 'widget-backward
        :ni     "RET" 'widget-button-press
        :ni     "down-mouse-1" 'widget-button-click
        :ni     "g" #'dashboard-insert-startupify-lists
        :ni     "a" (dashboard-insert-shortcut "a" "Agenda for today:")
        :ni     "r" (dashboard-insert-shortcut "r" "Recent Files:")
        :ni     "m" (dashboard-insert-shortcut "m" "Bookmarks:")
        :ni     "p" (dashboard-insert-shortcut "p" "Projects:"))))

;; from https://www.reddit.com/r/emacs/comments/8jaflq/tip_how_to_use_your_dashboard_properly/
(defun cpm/dashboard-banner ()
  "Set a dashboard banner including information on package initialization
   time and garbage collections."
  (setq dashboard-banner-logo-title
        (format "Emacs ready in %.2f seconds with %d garbage collections."
                (float-time (time-subtract after-init-time before-init-time)) gcs-done)))


(defun cpm/dashboard ()
 "load dashboard and swith to buffer"
(interactive)
(let ((buffer "*dashboard*"))
  (when (not (get-buffer buffer))
    (dashboard-insert-startupify-lists))
  (switch-to-buffer buffer)))

(defun goto-dashboard ()
  "goto the dashboard"
  (interactive)
  (switch-to-buffer "*dashboard*"))

Startup time

Display Startup time

(message "Start up time %.2fs" (float-time (time-subtract (current-time) my-start-time)))

Test

Ligatures

  (defun my-correct-symbol-bounds (pretty-alist)
    "Prepend a TAB character to each symbol in this alist,
this way compose-region called by prettify-symbols-mode
will use the correct width of the symbols
instead of the width measured by char-width."
    (mapcar (lambda (el)
              (setcdr el (string ?\t (cdr el)))
              el)
            pretty-alist))

  (defun my-ligature-list (ligatures codepoint-start)
    "Create an alist of strings to replace with
codepoints starting from codepoint-start."
    (let ((codepoints (-iterate '1+ codepoint-start (length ligatures))))
      (-zip-pair ligatures codepoints)))

  ; list can be found at https://github.com/i-tu/Hasklig/blob/master/GlyphOrderAndAliasDB#L1588
  (setq my-hasklig-ligatures
    (let* ((ligs '("&&" "***" "*>" "\\\\" "||" "|>" "::"
                   "==" "===" "==>" "=>" "=<<" "!!" ">>"
                   ">>=" ">>>" ">>-" ">-" "->" "-<" "-<<"
                   "<*" "<*>" "<|" "<|>" "<$>" "<>" "<-"
                   "<<" "<<<" "<+>" ".." "..." "++" "+++"
                   "/=" ":::" ">=>" "->>" "<=>" "<=<" "<->")))
      (my-correct-symbol-bounds (my-ligature-list ligs #Xe100))))

  ;; nice glyphs for haskell with hasklig
  (defun my-set-hasklig-ligatures ()
    "Add hasklig ligatures for use with prettify-symbols-mode."
    (setq prettify-symbols-alist
          (append my-hasklig-ligatures prettify-symbols-alist))
    (prettify-symbols-mode))

  (add-hook 'text-mode-hook 'my-set-hasklig-ligatures)