Instantly share code, notes, and snippets.

Embed
What would you like to do?
My primary emacs.d file

Useful documents

https://github.com/noctuid/evil-guide

Header

Ensure file is loaded with lexical binding enabled.

;;; -*- lexical-binding: t -*-

GUI settings

(when window-system
  (blink-cursor-mode 0) ; Disable the cursor blinking
  (scroll-bar-mode -1)  ; Disable the scroll bar
  (tool-bar-mode -1)    ; Disable the tool bar
  (tooltip-mode 0)      ; Disable the tooltips
  (menu-bar-mode -1))

Defaults

(setq-default
 ad-redefinition-action 'accept                   ; Silence warnings for redefinition
 auto-window-vscroll nil                          ; Lighten vertical scroll
 ;;confirm-kill-emacs 'yes-or-no-p                  ; Confirm before exiting Emacs
 cursor-in-non-selected-windows t                 ; Hide the cursor in inactive windows
 delete-by-moving-to-trash t                      ; Delete files to trash
 display-time-default-load-average nil            ; Don't display load average
 display-time-format "%H:%M"                      ; Format the time string
 fill-column 80                                   ; Set width for automatic line breaks
 help-window-select t                             ; Focus new help windows when opened
 indent-tabs-mode nil                             ; Stop using tabs to indent
 inhibit-startup-screen t                         ; Disable start-up screen
 initial-scratch-message ""                       ; Empty the initial *scratch* buffer
 left-margin-width 0 right-margin-width 0         ; Add left and right margins
 make-backup-files nil                            ; No backup files
 mouse-yank-at-point t                            ; Yank at point rather than pointer
 ns-use-srgb-colorspace nil                       ; Don't use sRGB colors
 recenter-positions '(5 top bottom)               ; Set re-centering positions
 scroll-margin 0                                  ; Add a margin when scrolling vertically
 select-enable-clipboard t                        ; Merge system's and Emacs' clipboard
 sentence-end-double-space t                      ; End a sentence after two spaces
 show-trailing-whitespace nil                     ; Display trailing whitespaces
 split-height-threshold nil                       ; Disable vertical window splitting
 split-width-threshold nil                        ; Disable horizontal window splitting
 tab-width 4                                      ; Set width for tabs
 uniquify-buffer-name-style 'forward              ; Uniquify buffer names
 window-combination-resize t                      ; Resize windows proportionally
 x-stretch-cursor t                               ; Stretch cursor to the glyph width
 visible-bell 1                                   ; Disable beeps
 bidi-display-reordering nil                      ; Make long lines faster by disabling bidi
 case-fold-search t                               ; Searches should be case insensitive
 visual-line-fringe-indicators t                  ; Show fringe indicator for visually wrapped lines
 evil-respect-visual-line-mode t                  ; visual j/k, gj/gk hard line move
 undo-tree-enable-undo-in-region nil)             ; Prevent problem in undo-in-region

(setq disabled-command-function nil)              ; Enable all disabled commands

(delete-selection-mode 1)                         ; Replace region when inserting text
(fset 'yes-or-no-p 'y-or-n-p)                     ; Replace yes/no prompts with y/n
(global-hl-line-mode 0)                           ; Hightlight current line
(global-subword-mode 0)                           ; Iterate through CamelCase words
(global-auto-revert-mode 1)                       ; Reload file if changed on disk
(mouse-avoidance-mode 'exile)                     ; Avoid collision of mouse with point if it gets too close
(put 'downcase-region 'disabled nil)              ; Enable downcase-region
(put 'upcase-region 'disabled nil)                ; Enable upcase-region
(fringe-mode '(8 . 0))

(cd "~/")                                         ; Move to the user directory

Common Lisp

(setq hfj/inferior-lisp-program "/usr/bin/sbcl")

Frames

(setq default-frame-alist
      '((left-fringe . 8)
        (right-fringe . 0)
        (menu-bar-lines . 0)
        (tool-bar-lines . 0)
        (horizontal-scroll-bars . nil)
        (vertical-scroll-bars . nil)
        (border-width . 0)))

Leaders

(setq hfj/main-leader-key "SPC"
      hfj/secondary-leader-key ",")

Org-mode src languages

Some languages that I use that can be overridden in the locals.org file. These will be available in “s” snippet in org modes.

(setq hfj/yas-org-src-languages '("emacs-lisp"
                                  "lisp"
                                  "scheme"
                                  "text"))

Helpers

(defmacro hfj/after-config (&rest body)
  `(add-hook 'hfj/post-config-hook #'(lambda () ,@body)))

Basic config

Emacs 26 VCS

(when (string= invocation-name "emacs-26-vcs")
  (setq with-editor-emacsclient-executable "/usr/bin/emacsclient-emacs-26-vcs"))

Locals

(setq vc-follow-symlinks nil)  ;; Prevent "follow link messages during startup"
(let ((org-filename (expand-file-name "locals.org" user-emacs-directory))
      (el-filename (expand-file-name "locals.el" user-emacs-directory)))
  (cond ((file-exists-p org-filename)
         (org-babel-load-file org-filename))
        ((file-exists-p el-filename)
         (load el-filename))))
(setq vc-follow-symlinks 'ask)  ;; Restore default

Remove startup info message

(defun display-startup-echo-area-message ()
  (message ""))

Appearance

Delight

(delight '((superword-mode "" t)
           (subword-mode "" t)
           (eldoc-mode nil "eldoc")))

Line numbering

;; (unless (version< emacs-version "26")
;;   (setq display-line-numbers-type 'relative)
;;   (add-hook 'prog-mode-hook 'display-line-numbers-mode))

Line wrapping

;; When a line is too long to display, wrap it.
(hfj/after-config
 (add-hook 'prog-mode-hook 'turn-on-visual-line-mode)
 (add-hook 'text-mode-hook 'turn-on-visual-line-mode)
 (add-hook 'org-mode-hook 'turn-on-visual-line-mode))

Modeline

(setq-default mode-line-format
              (list
               "%l" ; Line
               "%*" ; Modified marker
               "%c" ; Column
               '(:eval
                 (when (region-active-p)
                   (let* ((line-count (count-lines (region-beginning)
                                                   (min (1+ (region-end))
                                                        (point-max))))
                          (line-count-string (propertize
                                              (format "%d" line-count)
                                              'font-lock-face '(:foreground "White"))))
                     (cond ((= (region-beginning) (point))
                            (format " T%s#%s"
                                    (propertize (number-to-string (line-number-at-pos (region-end)))
                                                'face 'bold)
                                    line-count-string))
                           ((= (region-end) (point))
                            (format " F%s#%s"
                                    (propertize (number-to-string (line-number-at-pos (region-beginning)))
                                                'face 'bold)
                                    line-count-string))))))

               ;; Percent done
               " %p "

               ;; buffer name
               "%b "

               ;; Size
               "%I "

               ;; major mode
               "%m "

               ;; minor modes
               minor-mode-alist

               ;; filename
               '(:eval (when buffer-file-truename
                         (concat " " (file-name-directory buffer-file-truename))))))

(custom-set-faces
 '(mode-line ((t (:background "#2a2a28" :foreground "CadetBlue3" :box nil)))))

Themes

(use-package dracula-theme
  :demand t
  :init
  (load-theme 'dracula t t)
  :config
  (enable-theme 'dracula))
(use-package boron-theme
  :demand t
  :init
  (load-theme 'boron t t)
  (enable-theme 'boron)
  :config
  (custom-set-faces
   '(whitespace-trailing ((t (:background "dark gray" :foreground "#959595" :weight bold))))

   '(ediff-current-diff-A ((t (:background "#330d0d"))))
   '(ediff-fine-diff-A ((t (:background "#5a0d0d"))))

   '(ediff-current-diff-B ((t (:background "#0d330d"))))
   '(ediff-fine-diff-B ((t (:background "#0d4a0d"))))

   '(ediff-current-diff-C ((t (:background "#0d331d"))))
   '(ediff-fine-diff-C ((t (:background "#0d4a1d"))))

   '(ediff-odd-diff-A ((t (:background "grey5"))))
   '(ediff-even-diff-A ((t (:background "grey6"))))
   '(ediff-odd-diff-B ((t (:background "grey5"))))
   '(ediff-even-diff-B ((t (:background "grey6"))))
   '(ediff-odd-diff-C ((t (:background "grey5"))))
   '(ediff-even-diff-C ((t (:background "grey6"))))
   '(whitespace-indentation ((t (:background "grey11"))))))
(use-package ample-theme
  :demand t
  :init
  (load-theme 'ample t t)
  (load-theme 'ample-flat t t)
  (enable-theme 'ample-flat)
  :config
  (custom-set-faces
   '(whitespace-trailing ((t (:background "dark gray" :foreground "#959595" :weight bold))))
   '(ediff-current-diff-A ((t (:background "brown4"))))
   '(ediff-current-diff-B ((t (:background "dark olive green"))))
   '(ediff-current-diff-C ((t (:background "dark green"))))))

Custom file

Defaults/Overrides

(defvar hfj/markdown-command "~/.local/bin/pandoc")

Load

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

Evil

Main

(use-package evil
  :demand t
  :init
  (setq evil-want-integration nil)
  :config
  (evil-mode 1))

Escape

(use-package evil-escape
  :demand t
  :after evil
  :config
  (setq-default evil-escape-key-sequence "kl"
                evil-escape-unordered-key-sequence nil)
  (evil-escape-mode))

Indent-plus

(use-package evil-indent-plus
  :after evil
  :config
  (define-key evil-inner-text-objects-map "i" 'evil-indent-plus-i-indent)
  (define-key evil-outer-text-objects-map "i" 'evil-indent-plus-a-indent)
  (define-key evil-inner-text-objects-map "I" 'evil-indent-plus-i-indent-up)
  (define-key evil-outer-text-objects-map "I" 'evil-indent-plus-a-indent-up)
  (define-key evil-inner-text-objects-map "J" 'evil-indent-plus-i-indent-up-down)
  (define-key evil-outer-text-objects-map "J" 'evil-indent-plus-a-indent-up-down))

Keybindings

https://github.com/noctuid/general.el

(use-package general
  :demand t
  :load-path "local-packages/general.el/"
  :config
  (setq general-override-states '(insert
                                  emacs
                                  hybrid
                                  normal
                                  visual
                                  motion
                                  operator
                                  replace))
  (general-override-mode))

Numbers

(use-package evil-numbers
  :after evil
  :config
  (define-key evil-normal-state-map (kbd "C-c +") 'evil-numbers/inc-at-pt)
  (define-key evil-normal-state-map (kbd "C-c -") 'evil-numbers/dec-at-pt))

Surround

(use-package evil-surround
  :after evil
  :config
  (global-evil-surround-mode 1))

Keybindings

(use-package evil-collection
  :demand t
  :after evil
  :config
  ;; Make term buffers switch between char and line mode automatically.
  (setq evil-collection-term-sync-state-and-mode-p t)
  (evil-collection-init))

Functions

Binding

(defun hfj/spc-bind (keys func &optional alias)
  (let ((func-wrapper (cond (alias (list func :which-key alias))
                            (t func))))
    (general-define-key
     :states '(motion normal visual)
     :keymaps 'override
     :prefix hfj/main-leader-key
     keys func-wrapper)))

(defun hfj/spc-bind-on-major (keymaps keys func &optional alias)
  (let ((func-wrapper (cond (alias (list func :which-key alias))
                            (t func))))
    (general-define-key
     :states '(motion normal visual)
     :keymaps keymaps
     :prefix hfj/main-leader-key
     keys func-wrapper)))

(defun hfj/spc-declare-subtree (keys alias)
  (general-define-key
   :states '(motion normal visual)
   :keymaps 'override
   :prefix hfj/main-leader-key
   keys (list nil :which-key alias)))

(defun hfj/leader-bind (keymaps keys func &optional alias)
  (let ((func-wrapper (cond (alias (list func :which-key alias))
                            (t func))))
    (general-define-key
     :states '(normal visual)
     :keymaps keymaps
     :prefix hfj/secondary-leader-key
     keys func-wrapper)))

(defun hfj/minor-leader-bind (keymaps keys func &optional alias)
  (let ((func-wrapper (cond (alias (list func :which-key alias))
                            (t func))))
    (general-define-key
     :definer 'minor-mode
     :states '(normal visual)
     :keymaps keymaps
     :prefix hfj/secondary-leader-key
     keys func-wrapper)))

(defun hfj/leader-declare-subtree (keymaps keys alias)
  (general-define-key
   :states '(normal visual)
   :keymaps keymaps
   :prefix hfj/secondary-leader-key
   keys (list nil :which-key alias)))

Diagonals

(defun hfj/set-char-at-current-line-pos (ch insert-pos)
  (save-excursion
    (beginning-of-line)
    (let* ((start-pos (point))
           end-pos
           line-length)
      (end-of-line)
      (setf end-pos (point))
      (setf line-length (- end-pos start-pos 1))
      (cond ((< line-length insert-pos)
             (insert-char ?  (-  insert-pos line-length 1)))
            ((<= insert-pos line-length)
             (beginning-of-line)
             (incf (point) insert-pos)
             (delete-char 1)))
      (insert-char ch))))

(defun hfj/diagonal-text-up-left (text)
  "Replace text diagonally from current point."
  (interactive "sWhat text? ")
  (save-excursion
    (let* ((text-length (length text))
           (current-pos (point))
           (rev-text (reverse text))
           (line-pos (save-excursion (beginning-of-line) (- current-pos (point)))))
      (loop for ch in (append rev-text nil)
            for n downfrom line-pos to 0
            do (hfj/set-char-at-current-line-pos ch n)
            (previous-line)))))

(defun hfj/diagonal-text-up-right (text)
  "Replace text diagonally from current point."
  (interactive "sWhat text? ")
  (save-excursion
    (let* ((text-length (length text))
           (current-pos (point))
           (rev-text (reverse text))
           (line-pos (save-excursion (beginning-of-line) (- current-pos (point)))))
      (loop for ch in (append rev-text nil)
            for n from line-pos
            do (hfj/set-char-at-current-line-pos ch n)
            (previous-line)))))

(defun hfj/diagonal-text-down-right (text)
  "Replace text diagonally from current point."
  (interactive "sWhat text? ")
  (save-excursion
    (let* ((text-length (length text))
           (current-pos (point))
           (line-pos (save-excursion (beginning-of-line) (- current-pos (point)))))
      (loop for ch in (append text nil)
            for n from line-pos
            do (hfj/set-char-at-current-line-pos ch n)
            (next-line)))))

(defun hfj/diagonal-text-down-left (text)
  "Replace text diagonally from current point."
  (interactive "sWhat text? ")
  (save-excursion
    (let* ((text-length (length text))
           (current-pos (point))
           (line-pos (save-excursion (beginning-of-line) (- current-pos (point)))))
      (loop for ch in (append text nil)
            for n downfrom line-pos to 0
            do (hfj/set-char-at-current-line-pos ch n)
            (next-line)))))

Conversion

(defun hfj/point-seconds-to-date ()
  "Grab thing at point and attempt to convert it from Unix timestamp seconds to a readable date."
  (interactive)
  (let* ((word (thing-at-point 'word 'no-properties))
         (date-str (hfj/maybe-number-to-date word)))
    (kill-new date-str)
    (message "Date: %s" date-str)))

(defun hfj/maybe-number-to-date (str &optional format)
  (let ((num (string-to-number str))
        (format (or format "%F %H:%M:%S")))
    (if (and num (< 0 num))
        (format-time-string format num)
      (error "Failed to parse %s" str))))

Esc

;; 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))

Escaping

(defun hfj/dash-escape-file-name (name)
  "Convert filename into one without unacceptable characters or
spaces, replacing with dashes and underscores."
  (replace-regexp-in-string
   "--*"
   "-"
   (replace-regexp-in-string
    "\\."
    "-"
    (replace-regexp-in-string
     " "
     "-"
     (replace-regexp-in-string
      "/"
      "slash-"
      (replace-regexp-in-string
       "\\\\"  ;; \
       "_"
       name))))))

Eshell

(defun hfj/switch-to-eshell ()
  "Switch to a new eshell, or existing using completing-read to display existing buffers."
  (interactive)
  (cl-labels ((make-eshell-with-name (name)
                                     (let* ((window-to-use (selected-window))
                                            (template "*eshell-")
                                            (buffer-name (generate-new-buffer-name (concat template name "*")))
                                            (eshell-buffer (unwind-protect (eshell t))))
                                       (with-current-buffer eshell-buffer (rename-buffer buffer-name))
                                       (set-window-buffer window-to-use eshell-buffer))))
    (let* ((buffers (delete-if-not
                     #'(lambda (buf)
                         (eq (with-current-buffer buf major-mode) 'eshell-mode))
                     (with-persp-buffer-list () (buffer-list))))
           (buffer-names-raw (sort (mapcar #'buffer-name buffers) #'string<))
           (dirname (file-name-base (directory-file-name default-directory)))
           (dirname-listing (concat "New from current dir: " dirname))
           (buffer-names (cons dirname-listing buffer-names-raw))
           (selection (completing-read "Eshell buffers: "
                                       buffer-names
                                       nil  ; predicate
                                       nil  ; require-match
                                       nil  ; initial-input
                                       nil  ; hist
                                       'default ; default value
                                       )))
      (when selection
        (cond ((or (string= dirname-listing selection)
                   (eq selection 'default))
               (make-eshell-with-name dirname))
              ((member selection buffer-names)
               (switch-to-buffer selection))
              (t
               (make-eshell-with-name selection)))))))

(defun eshell/ll (&rest args)
  "ls -l"
  (eshell/ls (cons "-l" args)))

(defun eshell/la (&rest args)
  "ls -la"
  (eshell/ls (cons "-la" args)))

(defun eshell/e (&rest args)
  "Edit a file."
  (when (null args)
    (error "Missing file arguments"))
  (let ((file-paths (mapcar #'expand-file-name (eshell-flatten-list (reverse args)))))
    (mapc #'find-file file-paths)))

(defun eshell/rename-shell (new-name)
  (let* ((template "*eshell-")
         (name (generate-new-buffer-name (concat template new-name "*"))))
    (rename-buffer name)))

(defun hfj/eshell-insert-tramp-root ()
  (interactive)
  (insert-and-inherit
   (let* ((parts (s-split ":" default-directory))
          (parts-count (length parts)))
     (cond ((< 1 parts-count)
            (concat (s-join ":" (seq-take parts (1- parts-count))) ":/"))
           (t
            "/")))))

(defun hfj/find-eshell-root ()
  "Return the root directory for current path in eshell."
  (let* ((parts (s-split ":" default-directory))
         (parts-count (length parts)))
    (cond ((< 1 parts-count)
           (concat (s-join ":" (seq-take parts (1- parts-count))) ":/"))
          (t
           "/"))))

(defun hfj/eshell-hook ()
  (define-key eshell-mode-map (kbd "C-c /") 'hfj/eshell-insert-tramp-root)
  (setq pcomplete-cycle-completions nil))

Filename

(defun hfj/buffer-file-name-and-line ()
  (when buffer-file-name
    (concat (buffer-file-name) ":" (number-to-string (line-number-at-pos)))))

(defun hfj/show-and-copy-buffer-filename ()
  "Show the full path to the current file in the minibuffer and in the clipboard."
  (interactive)
  (let ((file-name (or (buffer-file-name)
                       (error "Buffer not visiting a file"))))
    (message file-name)
    (kill-new file-name)))

(defun hfj/show-and-copy-buffer-filename-and-line ()
  "Show the full path to the current file and line in the minibuffer and place in the clipboard."
  (interactive)
  (let ((file-name-and-line (or (hfj/buffer-file-name-and-line)
                                (error "Buffer not visiting a file"))))
    (message file-name-and-line)
    (kill-new file-name-and-line)))

Formatting

(defun hfj/remove-double-newlines ()
  "Replace multiple blank lines with a single one."
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "\\(^\\s-*$\\)\n" nil t)
    (replace-match "\n")
    (forward-char 1)))

Line Endings

(defun hfj/set-unix-line-endings ()
  "Change the current buffer to Unix line-ends."
  (interactive)
  (set-buffer-file-coding-system 'unix t))

(defun hfj/set-dos-line-endings ()
  "Change the current buffer to DOS line-ends."
  (interactive)
  (set-buffer-file-coding-system 'dos t))

(defun hfj/set-mac-line-endings ()
  "Change the current buffer to Mac line-ends."
  (interactive)
  (set-buffer-file-coding-system 'mac t))

Numeric

(defun hfj/base-convert (number from-base to-base)
  "Convert a number from one base to another."
  (interactive "sNumber to convert: \nnFrom base: \nnTo base: ")
  (let* ((full-numbers "0123456789abcdefghijklmnopqrstuvwxyz")
         (from-numbers (substring full-numbers 0 from-base))
         (to-numbers (substring full-numbers 0 to-base))
         (from-number (cl-loop for src-digit across (downcase (reverse number))
                               for poly from 0
                               for pos = (seq-position from-numbers src-digit)
                               do (when (null pos)
                                    (error "Invalid digit '%c'" src-digit))
                               sum (* pos (expt from-base poly))))
         (top-poly (cl-loop for poly from 0
                            for divisor = (expt to-base poly)
                            when (< from-number divisor)
                            return (1- poly)))
         (result (let ((to-digits '()))
                   (cl-loop for num = from-number then rem
                            for poly from top-poly downto 0
                            for divisor = (expt to-base poly)
                            for rem = (cl-rem num divisor)
                            for result = (/ num divisor)
                            do (push (elt to-numbers result) to-digits))
                   (seq-into (nreverse to-digits) 'string))))
    (kill-new result)
    (message "\"%s\" from base %d to base %d is \"%s\"" number from-base to-base result)
    result))

(defun hfj/show-binary-flags (number)
  "Convert number to bit flags and display in new buffer."
  (interactive "nNumber to explain: ")
  (let* ((top-poly (cl-loop for poly from 0
                            for divisor = (expt 2 poly)
                            when (< number divisor)
                            return (1- poly)))

         (flags (let ((to-digits '()))
                  (cl-loop for num = number then rem
                           for poly from top-poly downto 0
                           for divisor = (expt 2 poly)
                           for rem = (cl-rem num divisor)
                           for result = (/ num divisor)
                           do (push result to-digits))
                  to-digits))
         (new-buffer (generate-new-buffer (format "*Binary flags %d*" number))))

    (with-current-buffer new-buffer
      (insert (format "%d as binary flag\n\n" number))
      (insert "Position | Position Value\n")
      (insert "---------+----------------\n")
      (cl-loop for top = (expt 2 top-poly)
               for b in flags
               for i from 0
               for value = (expt 2 i)
               do (when (= b 1)
                    (insert (format "%8d | %d\n" i value))))
      (insert "\nAll:\n")
      (insert "Position | Flag | Position Value\n")
      (insert "---------+------+----------------\n")
      (cl-loop for top = (expt 2 top-poly)
               for b in flags
               for i from 0
               for value = (expt 2 i)
               do (insert (format "%8d | %d    | %d\n" i b value)))
      (setf (point) (point-min)))
    (display-buffer-below-selected new-buffer nil)))

Org

(defun hfj-org-mode/over-lines (f lines &optional initial-value)
  (do ((line-start 0 (1+ line-end))
       (line-end (search "\n" lines)
                 (search "\n" lines  :start2 (1+ line-end)))
       (acc initial-value))
      ((null line-end) acc)
    (setq acc
          (funcall f acc (substring lines line-start line-end)))))

(defun hfj-org-mode/get-org-block-contents ()
  (cl-labels
      ((get-block-contents
        ()
        (save-excursion
          (let ((start (re-search-backward "^[ \t]*#\\+BEGIN_SRC" nil t))
                (end (re-search-forward "^[ \t]*#\\+END_SRC" nil t)))
            (when end
              (setf (point) start)
              (setq start (search-forward "\n"))
              (setf (point) end)
              (setq end (1+ (search-backward "\n")))
              (buffer-substring start end)))))

       (find-trim-length (contents)
                         (hfj-org-mode/over-lines
                          #'(lambda (trim-length line)
                              (let* ((trim-line (s-trim-left line))
                                     (trim-line-lenth (length trim-line))
                                     (diff (- (length line) trim-line-lenth)))
                                (if (and (or (null trim-length) (< diff trim-length))
                                         (> trim-line-lenth 0))
                                    diff
                                  trim-length)))
                          contents))
       (trim-lines (contents trim-length)
                   (apply 'concat (nreverse (hfj-org-mode/over-lines
                                             #'(lambda (new-lines line)
                                                 (cons (concat (when (< trim-length (length line))
                                                                 (substring line trim-length))
                                                               "\n")
                                                       new-lines))
                                             contents))))
       (unescape (contents)
                 (replace-regexp-in-string "^\\([ \t]*\\),\\([*#]\\)"
                                           "\\1\\2"
                                           contents)))
    (let ((block-contents (get-block-contents)))
      (if (null block-contents)
          (error "Unable to find block.")
        (let ((trimmed-contents (let ((trim-length (find-trim-length block-contents)))
                                  (if (or (null trim-length) (= 0 trim-length))
                                      block-contents
                                    (trim-lines block-contents trim-length)))))
          (unescape trimmed-contents))))))

(defun hfj-org-mode/yank-org-src-block ()
  "Copy the contents of the current org-mode src block to the kill ring."
  (interactive)
  (cl-labels ((line-count (contents)
                          (hfj-org-mode/over-lines #'(lambda (acc line) (1+ acc)) contents 0))
              (inform (contents)
                      (let ((num-lines (line-count contents)))
                        (if (= 1 num-lines)
                            (message "Copied 1 line.")
                          (message "Copied %d lines." num-lines)))))
    (let ((to-kill (hfj-org-mode/get-org-block-contents)))
      (kill-new to-kill)
      (inform to-kill))))

Radio

(defun hfj/play-link (link)
  "Load link in new window"
  (split-window-below)
  (windmove-down)
  (let* ((arg-string (concat "mpv --quiet " (shell-quote-argument link) "; exit; \n"))
         (buffer (ansi-term (getenv "SHELL")))
         (proc (get-buffer-process buffer)))
    (comint-send-string buffer arg-string)
    (windmove-up)
    (set-process-sentinel proc
                          `(lambda (process event)
                             (when (string= event "finished\n")
                               (with-current-buffer ,buffer
                                 (kill-buffer-and-window)))))))

(defun hfj/make-link-player (link)
  #'(lambda ()
      (interactive)
      (hfj/play-link link)))

Path

(defun hfj/split-path (path)
  (s-split "/" path t))

(defun hfj/join-path (path-parts)
  (s-join "/" path-parts))

(defun hfj/is-subpath-of (path parent-path)
  "Determine if a path is child path of another.  When it is,
return the relative path from the parent."
  (let ((path-parts (hfj/split-path (expand-file-name path)))
        (parent-path-parts (hfj/split-path (expand-file-name parent-path))))
    (loop for p in path-parts
          and pp in parent-path-parts
          and remaining = path-parts then (cdr remaining)
          when (not (string= p pp))
          return nil
          finally return (hfj/join-path remaining))))

Shell

(defun hfj/eshell-here ()
  (interactive)
  (let ((window-to-use (selected-window))
        (eshell-buffer (unwind-protect (eshell t))))
    (set-window-buffer window-to-use eshell-buffer)))

(defun hfj/shell-here ()
  (interactive)
  (let ((window-to-use (selected-window))
        (config (current-window-configuration))
        (new-shell-buffer (generate-new-buffer (generate-new-buffer-name "*shell"))))
    (unwind-protect
        (shell new-shell-buffer)
      (set-window-configuration config))
    (set-window-buffer window-to-use new-shell-buffer)))

(defun hfj/switch-to-shell ()
  (interactive)
  (let* ((buffers (delete-if-not #'(lambda (buf)
                                     (eq (with-current-buffer buf major-mode) 'shell-mode))
                                 (with-persp-buffer-list () (buffer-list))))
         (buffer-names (sort (mapcar #'buffer-name buffers) #'string<)))
    (let ((selection (completing-read "Shell buffers: " buffer-names)))
      (when selection
        (cond ((member selection buffer-names)
               (switch-to-buffer selection))
              (t
               (let* ((window-to-use (selected-window))
                      (template "*shell-")
                      (config (current-window-configuration))
                      (new-shell-buffer (generate-new-buffer (concat template selection "*"))))
                 (unwind-protect
                     (shell new-shell-buffer)
                   (set-window-configuration config))
                 (set-window-buffer window-to-use new-shell-buffer))))))))

(defun hfj/ansi-term-here ()
  (interactive)
  (let ((window-to-use (selected-window))
        (config (current-window-configuration))
        (name (generate-new-buffer-name "*ansi-term"))
        (new-shell-buffer))
    (unwind-protect
        (setq new-shell-buffer (ansi-term "/bin/bash" name))
      (set-window-configuration config))
    (set-window-buffer window-to-use new-shell-buffer)))

(defun hfj/multi-term-here ()
  (interactive)
  (let ((window-to-use (selected-window))
        (config (current-window-configuration))
        (new-shell-buffer))
    (unwind-protect
        (setq new-shell-buffer (multi-term))
      (set-window-configuration config))
    (set-window-buffer window-to-use new-shell-buffer)))

String

Duplicate-n

(defun hfj/duplicate-n (times start)
  "Duplicate the text in the current kill n times.  The text of %i is replaced with the incrementing number."
  (interactive "nTimes to duplicate: \nnStarting at: ")
  (let ((text (car kill-ring-yank-pointer)))
    (dotimes (i times)
      (insert (s-replace-all `(("%i" . ,(number-to-string (+ i start)))) text)))))

Replace word at point

(defun hfj/replace-word-at-point ()
  (interactive)
  (er/mark-word)
  (let* ((str (buffer-substring (region-beginning) (region-end)))
         (replacement (read-string "Replacement: " str)))
    (replace-string str replacement t (point-min) (point-max))))

(defun hfj/replace-symbol-at-point ()
  (interactive)
  (er/mark-symbol)
  (let* ((str (buffer-substring (region-beginning) (region-end)))
         (replacement (read-string "Replacement: " str)))
    (replace-string str replacement t (point-min) (point-max))))

Change case

(defun hfj/snake-case->lower-camel-case (s)
  (let* ((words (split-string s "_"))
         (result (list (car words))))
    (dolist (word (cdr words) (s-join "" (nreverse result)))
      (push (capitalize (downcase word)) result))))

(defun hfj/snake-case->camel-case (s)
  (let (result)
    (dolist (word (split-string s "_") (s-join "" (nreverse result)))
      (push (capitalize (downcase word)) result))))

(defun hfj/underscorify (s &optional sep start)
  "Convert CamelCase to underscores."
  (let ((case-fold-search nil))
    (while (string-match "[A-Z]" s (or start 1))
      (setq s (replace-match (concat (or sep "_")
                                     (downcase (match-string 0 s)))
                             t nil s)))
    (downcase s)))

(defun hfj/filename-as-class ()
  (hfj/snake-case->camel-case
   (hfj/underscorify
    (file-name-base buffer-file-name))))

Generate random sequence (random username/password)

(defun hfj/seq-random-selection (seq out-length)
  "Create a new sequence from random elements of another sequence."
  (let ((len (length seq)))
    (coerce (cl-loop for n from 1 to out-length
                     for i = (random len)
                     collect (elt seq i))
            (type-of seq))))

(defun hfj/generate-basic-password (len)
  "Create a random password from uppercase and lowercase characters and numbers."
  (interactive "nPassword length: ")
  (let ((password (hfj/seq-random-selection
                   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
                   len)))
    (kill-new password)
    (message "Password copied to clipboard.")))

(defun hfj/generate-extended-password (len)
  "Create a random password from uppercase and lowercase characters, numbers, and funny chars."
  (interactive "nPassword length: ")
  (let ((password (hfj/seq-random-selection
                   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()\"' +-\\[]<>,./?;:{}|_=~`"
                   len)))
    (kill-new password)
    (message "Password copied to clipboard.")))

Symlinking

(defun hfj/symlink-to-alternate-dir (alternate-dir-name)
  (cl-labels ((make-path (rev-parts)
                (mapconcat 'identity (reverse rev-parts) "/"))
              (append-path (dir filename)
                (mapconcat 'identity (list dir filename) "/"))
              (find-alt-path (dir-name dir-parts &optional processed-parts)
                (if (null dir-parts)
                    nil
                  (let ((archive-path (make-path (cons dir-name dir-parts))))
                    (if (file-exists-p archive-path)
                        (append (reverse (cdr processed-parts))
                                (list dir-name)
                                dir-parts)
                      (find-alt-path dir-name
                                     (cdr dir-parts)
                                     (cons (car dir-parts)
                                           processed-parts))))))
              (depth-to-relative (depth)
                (if (> depth 0)
                    (concat "../" (depth-to-relative (1- depth)))
                  nil))
              (dup (val count)
                (unless (zerop count)
                  (cons val (dup val (1- count)))))
              (rel-path (from-parts to-parts &optional unique)
                (if (equal from-parts to-parts)
                    (make-path (append (reverse unique)
                                       (dup ".." (length unique))))
                  (rel-path (cdr from-parts)
                            (cdr to-parts)
                            (cons (car from-parts) unique)))))
    (let* ((parts (split-string buffer-file-name "/"))
           (rev-parts (reverse parts))
           (file-name (car rev-parts))
           (dir-parts (cdr rev-parts))
           (archive-dir-parts (find-alt-path alternate-dir-name dir-parts)))
      (if (null archive-dir-parts)
          (error "Unable to find alternate path.")
        (let* ((target-dir (make-path archive-dir-parts))
               (target-path (append-path target-dir file-name))
               (source-dir (rel-path dir-parts archive-dir-parts))
               (source-path (append-path source-dir file-name)))

          (make-symbolic-link (mapconcat 'identity
                                         (list (rel-path dir-parts archive-dir-parts)
                                               file-name)
                                         "/")
                              (make-path (cons file-name archive-dir-parts))))))))

Timer

(defun hfj/parse-short-time (time-string)
  "Convert short time form to seconds.  Example \"1h1m\" is 3660.

s: seconds
m: minutes
h: hours
d: days
w: weeks
M: months
y: years"
  (let ((seconds 0)
        (start 0)
        (numbers nil)
        (operator nil)
        (total 0)
        (time-string-length (length time-string)))
    (save-match-data
      (while (< start time-string-length)
        (cond ((string-match "\\([0-9]+\\)\\([smhdw]\\)" time-string start)
               (let* ((numbers (string-to-number (match-string 1 time-string)))
                      (operator (match-string 2 time-string))
                      (multiplier (cond ((string= operator "s") 1)
                                        ((string= operator "m") 60)
                                        ((string= operator "h") (* 60 60))
                                        ((string= operator "d") (* 60 60 24))
                                        ((string= operator "w") (* 60 60 24 7))
                                        ((string= operator "M") (* 60 60 24 30))
                                        ((string= operator "y") (* 60 60 24 365))
                                        (t (error "Invalid multiplier %s" operator)))))
                 (setq start (match-end 0))
                 (setq total (+ total (* numbers multiplier)))))
              (t
               (error "Invalid time pattern: %s" (substring time-string start))))))
    total))

(defun hfj/message-at-time (msg time)
  "Message user at a certain time."
  (interactive "sMessage: \nsTime (##:## or # second/minute): ")
  (let ((time-or-seconds (or (ignore-errors (hfj/parse-short-time time))
                             time))
        (start-time (current-time)))
    (run-at-time time-or-seconds nil
                 (lambda ()
                   (let ((output-msg (format "Timer message from %s ago:\n%s "
                                             (format-seconds "%Y, %D, %H, %M, %z%S"
                                                             (float-time (time-subtract (current-time) start-time)))
                                             msg)))
                     (start-process "notify-send" nil "notify-send" output-msg)
                     (message-box output-msg))))))

Languages

C/C++

(defun hfj/c-mode-common-hook ()
  (modify-syntax-entry ?_ "w" c-mode-syntax-table)
  (modify-syntax-entry ?_ "w" c++-mode-syntax-table)

  (dolist (mode '(c-mode-map c++-mode-map))
    (hfj/leader-bind mode "o" 'ff-get-other-file "visit other file")))

(add-hook 'c-mode-common-hook 'hfj/c-mode-common-hook)

Common Lisp

(use-package slime
  :defer 1
  :init
  (setq inferior-lisp-program hfj/inferior-lisp-program)
  (add-hook 'slime-repl-mode-hook #'hfj/slime-repl-mode-hook)
  ;; (add-hook 'slime-load-hook #'hfj/slime-mode-hook)

  ;; Start slime on startup
  ;; (save-window-excursion
  ;;   (slime))

  ;; Lisp files connect to slime automatically
  (add-hook 'slime-mode-hook
            (lambda ()
              (unless (slime-connected-p)
                (save-excursion (slime)))))
  :config
  (slime-setup '(slime-fancy
                 slime-company
                 slime-indentation)) ;; slime overrides cl-indent without slime-indentation
  (defmacro slime-eval-in-thread (&rest body)
    `(slime-eval
      `(cl:progn
        (bt:make-thread
         (cl:lambda () ,,@body))
        nil))))

;; Example usage:
;;   (slime-eval-in-thread
;;    `(cl:progn (hackmud:send-text ,(concat "user " user))
;;               (cl:sleep 1.9)
;;               (hackmud:send-text-q "sys.specs")
;;               (cl:sleep 1.5)
;;               (hackmud:send-text-q "accts.balance")))

(defun hfj/slime-repl-mode-hook ()
  (slime-eval
   `(cl:progn
      (ql:quickload :bt-semaphore :silent t)
      (cl:setf cl:*read-default-float-format* 'cl:double-float))))

(use-package slime-company
  :after slime)

(defun hfj/common-lisp-mode-hook ()
  (put 'defcommand 'common-lisp-indent-function (get 'defun 'common-lisp-indent-function))
  (set (make-local-variable 'lisp-indent-function) 'common-lisp-indent-function))

;; Indent common lisp code correctly
(add-hook 'lisp-mode-hook 'hfj/common-lisp-mode-hook)

Emacs Lisp

(defun hfj/emacs-lisp-mode-hook ()
  (modify-syntax-entry ?- "w" emacs-lisp-mode-syntax-table)
  (modify-syntax-entry ?/ "w" emacs-lisp-mode-syntax-table)
  (dolist (pair '(;; ("<=" .    #x2264) ; ≤
                  ;; (">=" .    #x2265) ; ≥
                  ("defun" . #x2a0d) ;
                  ))
    (push pair prettify-symbols-alist))
  (turn-on-prettify-symbols-mode)
  (smartparens-mode)
  ;; Don't pair single quotes
  (sp-local-pair 'emacs-lisp-mode "'" nil :actions nil)
  (aggressive-indent-mode))

(use-package emacs-lisp-mode
  :ensure nil
  :delight emacs-lisp-mode "Emacs Lisp"
  :init
  (add-hook 'emacs-lisp-mode-hook 'hfj/emacs-lisp-mode-hook)
  :config
  (delight 'lisp-interaction-mode "Lisp Interaction"))

(use-package ielm
  :ensure nil
  :hook (ielm-mode . (lambda () (setq-local scroll-margin 0))))

Haskell

(use-package haskell-mode
  :mode (("\\.hs$" . haskell-mode)
         ("\\.lhs$" . haskell-mode)
         ("\\.hsc$" . haskell-mode)
         ("\\.cpphs$" . haskell-mode)
         ("\\.c2hs$" . haskell-mode)))
(use-package intero
  :after haskell-mode
  :hook (haskell-mode . intero-mode))

Javascript

(use-package js2-mode
  :mode (("\\.js" . js2-mode))
  :config
  (add-hook 'js2-mode-hook #'js2-imenu-extras-mode)
  (add-hook 'js2-mode-hook #'hfj/js2-hook))

(use-package js2-refactor
  :after js2-mode)

(use-package xref-js2
  :after js2-mode)

(with-eval-after-load 'js2-mode
  (with-eval-after-load 'smartparens
    (sp-with-modes '(js2-mode)
      (sp-local-pair "/*" "*/" :post-handlers '(:add ("||\n[i]" "RET")))
      (sp-local-pair "{" nil :post-handlers '(:add ("||\n[i]" "RET")))
      (sp-local-pair "(" nil :post-handlers '(:add ("||\n[i]" "RET"))))))

(defun hfj/js2-hook ()
  (smartparens-mode)
  (turn-on-visual-line-mode)
  (setq indent-tabs-mode nil
        tab-width 4
        c-basic-offset 4)
  (company-mode t)
  (font-lock-add-keywords nil
                          `((,(rx (not (any ?= ?!)) (group ?=) (not (any ?= ?> ?<))) 1 'hi-red-b prepend))))

Lisp

(defun hfj/lisp-mode-hook ()
  (modify-syntax-entry ?- "w" lisp-mode-syntax-table)
  (modify-syntax-entry ?/ "w" lisp-mode-syntax-table)
  (dolist (pair '(;; ("<=" .    #x2264) ; ≤
                  ;; (">=" .    #x2265) ; ≥
                  ("defun" . #x2a0d) ;
                  ))
    (push pair prettify-symbols-alist))
  (turn-on-prettify-symbols-mode)
  (smartparens-mode)
  ;; Don't pair single quotes
  (sp-local-pair 'lisp-mode "'" nil :actions nil)
  (aggressive-indent-mode)
  (require 'slime)
  (require 'slime-company))

(use-package lisp-mode
  :ensure nil
  :delight lisp-mode "Lisp"
  :init
  (add-hook 'lisp-mode-hook 'hfj/lisp-mode-hook))

Markdown

(use-package markdown-mode
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command hfj/markdown-command))

PHP

(use-package php-mode
  :mode (("\\.php" . php-mode))
  :init
  (add-hook 'php-mode-hook 'hfj/php-mode-hook)
  :config
  (setq php-template-compatibility nil))

(use-package company-php
  :after php-mode)

(use-package ac-php
  :after php-mode)

(with-eval-after-load 'php-mode
  (with-eval-after-load 'smartparens
    (sp-with-modes '(php-mode)
      (sp-local-pair "/**" "*/" :post-handlers '(("| " "SPC")
                                                 (hfj/php-handle-docstring "RET")))
      (sp-local-pair "/*." ".*/" :post-handlers '(("| " "SPC")))
      (sp-local-pair "{" nil :post-handlers '(:add ("||\n[i]" "RET")))
      (sp-local-pair "(" nil :post-handlers '(:add ("||\n[i]" "RET"))))))

(defun hfj/php-mode-hook ()
  (require 'ac-php)
  (require 'company-php)
  (smartparens-mode)
  (setq indent-tabs-mode nil
        tab-width 4
        c-basic-offset 4)
  (company-mode t)
  (ac-php-core-eldoc-setup) ;; enable eldoc
  (make-local-variable 'company-backends)
  (add-to-list 'company-backends 'company-ac-php-backend)
  (modify-syntax-entry ?_ "w" php-mode-syntax-table)
  (font-lock-add-keywords nil
                          `((,(rx (not (any ?= ?! ?> ?< ?* ?+ ?- ?/))
                                  (group (zero-or-one (any ?* ?+ ?- ?/)) ?=)
                                  (not (any ?= ?> ?<)))
                             1  ;; group
                             'hi-red-b  ;; color
                             prepend))))

(defun hfj/php-handle-docstring (&rest _ignored)
  (-when-let (line (save-excursion
                     (forward-line)
                     (thing-at-point 'line)))
    (cond
     ;; variable
     ((string-match (rx (or "private" "protected" "public" "var") (1+ " ") (group "$" (1+ alnum))) line)
      (let ((var-name (match-string 1 line))
            (type ""))
        ;; try to guess the type from the constructor
        (-when-let (constructor-args (my-php-get-function-args "__construct" t))
          (setq type (or (cdr (assoc var-name constructor-args)) "")))
        (insert "* @var " type)
        (save-excursion
          (insert "\n"))))
     ((string-match-p "function" line)
      (save-excursion
        (let ((args (save-excursion
                      (forward-line)
                      (my-php-get-function-args nil t))))
          (--each args
            (when (my-php-should-insert-type-annotation (cdr it))
              (insert (format "* @param %s%s\n"
                              (my-php-translate-type-annotation (cdr it))
                              (car it))))))
        (let ((return-type (save-excursion
                             (forward-line)
                             (my-php-get-function-return-type))))
          (when (my-php-should-insert-type-annotation return-type)
            (insert (format "* @return %s\n" (my-php-translate-type-annotation return-type))))))
      (re-search-forward (rx "@" (or "param" "return") " ") nil t))
     ((string-match-p ".*class\\|interface" line)
      (save-excursion (insert "\n"))
      (insert "* ")))
    (let ((o (sp--get-active-overlay)))
      (indent-region (overlay-start o) (overlay-end o)))))

Racket

(defun hfj/racket-mode-hook ()
  (smartparens-mode)
  ;; Don't pair single quotes
  (sp-local-pair 'racket-mode "'" nil :actions nil)
  (aggressive-indent-mode)
  (rainbow-delimiters-mode)
  (show-smartparens-mode))

(defun hfj/racket-repl-mode-hook ()
  (smartparens-mode)
  ;; Don't pair single quotes
  (sp-local-pair 'racket-mode "'" nil :actions nil)
  (company-mode))

(use-package racket-mode
  :mode (("\\.rkt" . racket-mode))
  :delight racket-mode "Racket"
  :init
  (add-hook 'racket-mode-hook 'hfj/racket-mode-hook)
  (add-hook 'racket-repl-mode-hook 'hfj/racket-repl-mode-hook))

Scheme

(defun hfj/scheme-mode-hook ()
  (modify-syntax-entry ?- "w" scheme-mode-syntax-table)
  (modify-syntax-entry ?/ "w" scheme-mode-syntax-table)
  ;; Don't pair single quotes
  (sp-local-pair 'scheme-mode "'" nil :actions nil)
  (smartparens-mode)
  (aggressive-indent-mode)
  (rainbow-delimiters-mode)
  (show-smartparens-mode))

(use-package scheme-mode
  :ensure nil
  :delight scheme-mode "Scheme"
  :mode (("\\.scm" . scheme-mode)
         ("\\.ss" . scheme-mode))
  :init
  (add-hook 'scheme-mode-hook 'hfj/scheme-mode-hook)
  (setq scheme-program-name "gosh -i"))

Shell scripts

(use-package sh-mode
  :mode (("\\.sh" . sh-mode))
  :ensure nil
  :init
  (add-hook 'sh-mode-hook 'hfj/sh-mode-hook))

(defun hfj/sh-mode-hook ()
  (smartparens-mode)
  (sp-local-pair 'sh-mode "{" nil :post-handlers '(:add ("||\n[i]" "RET")))
  (sp-local-pair 'sh-mode "(" nil :post-handlers '(:add ("||\n[i]" "RET"))))

Features

Alert

(use-package alert
  :commands alert
  :init
  (defun alert-long (&rest rest)
    (let ((alert-fade-time 0))
      (apply #'alert rest)))
  :config
  (setq alert-default-style 'libnotify))

Annotate

(use-package annotate
  :defer 1
  :commands (annotate-mode annotate-annotate))

Auto-complete (company)

Company

(defvar hfj/company-fill-column nil)
(defun hfj/company-start-hook (action)
  )

(defun hfj/company-stop-hook (result)
  )

(use-package company
  :commands company-mode
  :init
  (add-hook 'prog-mode-hook 'company-mode)
  :config
  (setq-default
   company-idle-delay .2
   company-minimum-prefix-length 1
   company-require-match nil
   company-tooltip-align-annotations t)
  (company-quickhelp-mode)
  (add-hook 'company-completion-started-hook 'hfj/company-start-hook)
  (add-hook 'company-completion-finished-hook 'hfj/company-stop-hook)
  (add-hook 'company-completion-cancelled-hook 'hfj/company-stop-hook))

(use-package company-quickhelp
  :after company)

(use-package company-dabbrev
  :demand t
  :ensure nil
  :after company
  :config (setq-default
           ;; Use case found instead of adjusting case
           company-dabbrev-downcase nil
           ;; Limit search to buffers of same mode
           company-dabbrev-other-buffers t))

Company-dictionary

(use-package company-dict
  :demand t
  :after company
  :config
  ;; Where to look for dictionary files. Default is ~/.emacs.d/dict
  (setq company-dict-dir (concat user-emacs-directory "dict/"))

  ;; Optional: if you want it available everywhere
  (add-to-list 'company-backends 'company-dict)

  ;; Optional: evil-mode users may prefer binding this to C-x C-k for vim
  ;; omni-completion-like dictionary completion
  (define-key evil-insert-state-map (kbd "C-c TAB") 'company-dict))

Auto-revert

(setq auto-revert-interval 5)
(auto-revert-set-timer)
(add-hook 'dired-mode-hook 'auto-revert-mode)

Ace Window

(use-package ace-window
  :demand t
  :after key-chord
  :init
  (custom-set-faces
   '(aw-leading-char-face ((t (:background "black" :foreground "white" :underline t :weight extra-bold)))))
  (key-chord-define evil-normal-state-map "al" 'ace-window)
  (key-chord-define evil-normal-state-map "la" 'ace-window)
  :config
  (setq aw-keys '(?a ?s ?d ?f ?h ?j ?k ?l)
        aw-background nil
        aw-dispatch-always t))

Avy

(use-package avy
  :demand t
  :after key-chord
  :config
  (setq avy-keys '(?a ?s ?d ?f ?h ?j ?k ?l))
  (custom-set-faces
   '(avy-lead-face ((t (:background "white" :foreground "gray1"))))
   '(avy-lead-face-0 ((t (:background "white" :foreground "gray3"))))
   '(avy-lead-face-1 ((t (:background "white" :foreground "gray5"))))
   '(avy-lead-face-2 ((t (:background "white" :foreground "gray7"))))))

Bindings

(use-package which-key
  :demand t
  :config (which-key-mode))

Centered Cursor

(use-package centered-cursor-mode
  :commands centered-cursor-mode)

Commenting

(use-package evil-nerd-commenter
  :commands evilnc-comment-or-uncomment-lines)

Dired

Add human readable and place directories at top.

(setq dired-listing-switches "-ahl  --group-directories-first")

Ediff

;; Ediff should not open in separate window.
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

;; Ediff looks better horizontally normally
(setq ediff-split-window-function 'split-window-horizontally)

Editor Config

http://editorconfig.org/

(use-package editorconfig
  :config
  (editorconfig-mode 1))

Eshell

(use-package eshell
  :ensure nil
  :commands (eshell eshell-mode)
  :config
  (setq hfj/eshell-date-regex (rx (repeat 4 digit) "-" (repeat 2 digit) "-" (repeat 2 digit) " " (repeat 2 digit) ":" (repeat 2 digit))
        eshell-prompt-regexp (concat (rx bol)
                                     hfj/eshell-date-regex
                                     (rx " " (regexp "[^#$\n]* [#$] ")))
        eshell-prompt-function #'(lambda ()
                                   (concat (format-time-string "%Y-%m-%d %H:%M")
                                           " "
                                           (abbreviate-file-name (eshell/pwd))
                                           " " (if (= (user-uid) 0) "#" "$") " ")))
  (add-hook 'eshell-mode-hook 'hfj/eshell-hook))

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

Expand region

https://github.com/magnars/expand-region.el

(use-package expand-region
  :demand t)

Flycheck

(use-package flycheck
  :init
  (global-flycheck-mode)
  :config
  (add-hook 'after-init-hook #'global-flycheck-mode)
  (setf flycheck-display-errors-delay 1.0)
  (setq flycheck-display-errors-function
        #'flycheck-display-error-messages-unless-error-list)
  (setq flycheck-navigation-minimum-level 'error))

Flyspell

(defun hfj/enable-flyspell ()
  (flyspell-mode 1))
(defun hfj/disable-flyspell ()
  (flyspell-mode -1))

(dolist (hook '(text-mode-hook org-mode-hook))
  (add-hook hook #'hfj/enable-flyspell))
(dolist (hook '(change-log-mode-hook log-edit-mode-hook))
  (add-hook hook #'hfj/disable-flyspell))
(add-hook 'prog-mode-hook 'flyspell-prog-mode)

Garbage collection

(add-hook 'focus-out-hook #'garbage-collect)

Geiser

(use-package geiser
  :commands (run-geiser)
  :init
  (setq geiser-repl-use-other-window nil
        geiser-default-implementation 'racket))

GPG

(require 'auth-source)
;; This is required to get a GPG prompt
(setq epa-pinentry-mode 'loopback)

Helm

Main

(use-package helm
  :demand t
  :config
  (helm-mode 1)
  (setq-default helm-split-window-in-side-p           t ; open helm buffer inside current window, not occupy whole other window
                helm-ff-search-library-in-sexp        t ; search for library in `require' and `declare-function' sexp.
                helm-scroll-amount                    8 ; scroll 8 lines other window using M-<next>/M-<prior>
                helm-ff-file-name-history-use-recentf t
                helm-echo-input-in-header-line t)
  (setq helm-candidate-number-limit 250)
  (define-key helm-map (kbd "C-r") 'evil-paste-from-register))

Helm Command

(use-package helm-command
  :ensure nil
  :after helm
  :bind ([remap execute-extended-command] . helm-M-x)
  :config
  (setq-default helm-M-x-fuzzy-match t))

Helm Files

(use-package helm-files
  :ensure nil
  :after helm
  :bind ([remap find-file] . helm-find-files)
  :config
  (setq-default
   helm-ff-no-preselect t
   helm-ff-skip-boring-files t
   helm-find-file-ignore-thing-at-point t))

Helm Swoop

(use-package helm-swoop
  :after helm
  :commands helm-swoop
  :config
  (setq helm-swoop-split-direction 'split-window-horizontally))

Helm Ag

(use-package helm-ag
  :after helm)

Highlight

(global-hi-lock-mode 1)
(setq hi-lock-file-patterns-policy #'(lambda (dummy) t))

(use-package highlight-thing
  :init
  (add-hook 'prog-mode-hook 'highlight-thing-mode)
  :config
  (setq highlight-thing-exclude-thing-under-point t
        highlight-thing-case-sensitive-p t)
  (setq highlight-thing-ignore-list
        (append highlight-thing-ignore-list
                '("*"
                  "**"
                  "***"
                  "****"
                  "*****"
                  "******"
                  "*******")))
  (face-spec-set 'highlight-thing
                 '((t (:inherit 'hi-green-b)))))

Hydra

Main

(use-package hydra
  :after evil
  :config
  (setq-default hydra-default-hint nil))

(cl-defun hfj/dynamic-format-hydra-doc (title parts &optional (max-width 80))
  (cl-labels ((do-replacements (str)
                               (replace-regexp-in-string (rx "%s(") "("
                                                         (replace-regexp-in-string (rx "_" (group (not (any "_"))) "_") "\\1" str)))
              (get-length (part)
                          (let ((count 0)
                                (pops '()))
                            (cl-loop for ch across (do-replacements part)
                                     do (cond ((and pops (= ch (car pops)))
                                               (setq pops (cdr pops)))
                                              ((= ?_ ch)
                                               (push ?_ pops))
                                              ((= ?( ch)
                                                  (when (null pops)
                                                    (incf count))
                                                  (push ?) pops))
                                              ((null pops)
                                               (incf count)))
                                     finally (return count)))))
    (let* ((parts-lengths (map 'list #'get-length parts))
           (output-lines '()))
      (cl-labels ((try-height (height)
                              (let ((column-width 0)
                                    (total-width 0)
                                    (parts-count 0))
                                (dolist (part parts)
                                  (when (= 0 (cl-rem parts-count height))
                                    (incf total-width column-width)
                                    (setq column-width 0))
                                  (incf parts-count)
                                  (setq column-width (max (get-length part) column-width)))
                                (incf total-width column-width)
                                (message "Total width: %d" total-width)
                                (<= total-width max-width)))
                  )
        (let ((recommended-height (cl-loop for height from 1 below (length parts)
                                           until(try-height height)
                                           finally (return height)))
              (lines '())
              (row-width 0))
          (cl-loop for n from 1 to recommended-height
                   do (push (string) lines))
          (message "Blank lines: %s" lines)
          (cl-loop for n from 0 below (length parts)
                   for row-n = (cl-rem n recommended-height)
                   do (progn
                        (message "Row-n: %s" row-n)
                        (when (= 0 row-n)
                          (setq row-width (cl-loop for i from 0 below recommended-height
                                                   while (< (+ i n) (length parts))
                                                   maximizing (length (elt parts (+ i n)))))

                          (message "Row-width: %s" row-width)
                          )
                        (setf (elt lines row-n)
                              (format "%s%s%s "
                                      (elt lines row-n)
                                      (elt parts n)
                                      (make-string (- row-width (length (elt parts n))) ? )))
                        (message "Lines now: %s" lines))
                   finally (return (format "%s\n%s" title (s-join "\n" lines)))))))))

git-gutter

(defun hfj/gg-stat ()
  (let ((s (git-gutter:statistic)))
    (format "Added %d lines, Deleted %d lines" (car s) (cdr s))))

(defhydra hydra-git-gutter (:hint nil)
  "
Git gutter %s(hfj/gg-stat)

^Navigation^      ^Actions^       ^Actions^
^-^---------------^-^-------------^-^------
_n_: next         _s_: stage      _m_: mark
_p_: previous     _R_: revert
"
  ("q" nil)
  ("n" hfj/git-gutter-next-hunk)
  ("p" hfj/git-gutter-prev-hunk)
  ("s" git-gutter:stage-hunk)
  ("m" git-gutter:mark-hunk)
  ("R" git-gutter:revert-hunk))

outline

(defhydra hydra-outline ()
  "Outline"
  ("q" nil)

  ("<tab>" outline-cycle "cycle")
  ("<backtab>" outshine-cycle-buffer "cycle all")

  ("j" outline-next-visible-heading)
  ("k" outline-previous-visible-heading)

  ("gj" outline-forward-same-level)
  ("gk" outline-backward-same-level)

  ("h" outline-up-heading)
  ("l" outline-next-heading)

  ("K" outline-move-subtree-up)
  ("J" outline-move-subtree-down)

  ("L" outline-demote)
  ("H" outline-promote)

  ("n" outshine-narrow-to-subtree "narrow")
  ("w" widen "widen"))

persp-mode

(defmacro hfj/make-tab (name &rest body)
  "Select an existing tab, or create one and configure it."
  `(cond
    ((persp-with-name-exists-p ,name)
     (persp-switch ,name))
    (t
     (persp-switch ,name)
     ,@body)))

(defun hfj/make-tab-f (name setup-actions)
  "Select an existing tab, or create one and configure it."
  (cond
   ((persp-with-name-exists-p name)
    (persp-switch name))
   (t
    (persp-switch name)
    (funcall setup-actions))))

(defun define-layout-inner (name f)
  "Add layout config to hfj/predefined-layouts"
  (setq hfj/predefined-layouts
        (delete-if #'(lambda (a) (string-equal (car a) name))
                   hfj/predefined-layouts))
  (push (list* name (cons name f)) hfj/predefined-layouts)
  (setq hfj/predefined-layouts
        (sort hfj/predefined-layouts
              #'(lambda (a b) (string< (car a) (car b))))))

(defmacro hfj/define-layout (name &rest body)
  "Add layout config to hfj/predefined-layouts"
  `(define-layout-inner ,name #'(lambda () ,@body)))

(defun hfj/pick-layout ()
  "Switch to a new or existing layout."
  (interactive)
  (let* ((names (persp-names))
         (name (completing-read "Switch to layout: " names))
         (exists (persp-with-name-exists-p name)))
    (persp-switch name)
    (unless exists
      (switch-to-buffer "*scratch*"))))

(defvar hfj/predefined-layouts '())

(defun hfj/pick-predefined-layout ()
  "Create a predefined layout to be selectable from list."
  (interactive)
  (when (null hfj/predefined-layouts)
    (error "No layouts configured."))

  (let ((layout-name-and-actions (helm :sources (helm-build-sync-source "layout"
                                                  :candidates hfj/predefined-layouts))))

    (when layout-name-and-actions
      (hfj/make-tab-f (car layout-name-and-actions) (cdr layout-name-and-actions)))))

(defun hfj/persp-kill-current ()
  (interactive)
  (let ((persp (get-current-persp)))
    (cond ((null persp) (error "Unable to kill default layout."))
          (t (persp-kill (persp-name persp))))))

(defun hfj/persp-switch-to-n (n)
  (let ((names (persp-names-current-frame-fast-ordered))
        (count 1))
    (dolist (name names)
      (when (= count n)
        (persp-switch name))
      (cl-incf count))))
(defun hfj/persp-switch-to-1 () (interactive) (hfj/persp-switch-to-n 1))
(defun hfj/persp-switch-to-2 () (interactive) (hfj/persp-switch-to-n 2))
(defun hfj/persp-switch-to-3 () (interactive) (hfj/persp-switch-to-n 3))
(defun hfj/persp-switch-to-4 () (interactive) (hfj/persp-switch-to-n 4))
(defun hfj/persp-switch-to-5 () (interactive) (hfj/persp-switch-to-n 5))
(defun hfj/persp-switch-to-6 () (interactive) (hfj/persp-switch-to-n 6))
(defun hfj/persp-switch-to-7 () (interactive) (hfj/persp-switch-to-n 7))
(defun hfj/persp-switch-to-8 () (interactive) (hfj/persp-switch-to-n 8))
(defun hfj/persp-switch-to-9 () (interactive) (hfj/persp-switch-to-n 9))
(defun hfj/persp-switch-to-10 () (interactive) (hfj/persp-switch-to-n 10))

(defun hydra-perse-names ()
  (let ((names (persp-names-current-frame-fast-ordered))
        (current-name (safe-persp-name (get-current-persp)))
        (parts '())
        (count 1))
    (dolist (name names (s-join " | " (nreverse parts)))
      (cond ((eq name current-name)
             (push (format "[%d:%s]" count name) parts))
            (t
             (push (format "%d:%s" count name) parts)))
      (cl-incf count))))

(defhydra hydra-persp (:hint nil)
  "
Layouts %s(hydra-perse-names)

^Navigation^      ^Selection^       ^Actions^        ^Buffers^
^-^---------------^-^---------------^-^--------------^-^------------
_n_: next         _l_: choose       _d_: delete      _a_: add buffer
_p_: previous     _L_: predefined   _r_: rename
"
  ("q" nil)
  ("a" persp-add-buffer :exit t)
  ("d" hfj/persp-kill-current)
  ("l" hfj/pick-layout :exit t)
  ("L" hfj/pick-predefined-layout :exit t)
  ("r" persp-rename :exit t)
  ("n" persp-next)
  ("p" persp-prev)
  ("1" hfj/persp-switch-to-1 :exit t)
  ("2" hfj/persp-switch-to-2 :exit t)
  ("3" hfj/persp-switch-to-3 :exit t)
  ("4" hfj/persp-switch-to-4 :exit t)
  ("5" hfj/persp-switch-to-5 :exit t)
  ("6" hfj/persp-switch-to-6 :exit t)
  ("7" hfj/persp-switch-to-7 :exit t)
  ("8" hfj/persp-switch-to-8 :exit t)
  ("9" hfj/persp-switch-to-9 :exit t)
  ("0" hfj/persp-switch-to-10 :exit t))

smartparens

(defhydra hydra-smartparens ()
  "Smartparens"
  ("q" nil)

  ;; Wrapping
  ("(" (lambda (_) (interactive "P") (sp-wrap-with-pair "(")))
  ("{" (lambda (_) (interactive "P") (sp-wrap-with-pair "{")))
  ("'" (lambda (_) (interactive "P") (sp-wrap-with-pair "'")))
  ("\"" (lambda (_) (interactive "P") (sp-wrap-with-pair "\"")))

  ("w" (lambda (_) (interactive "P") (sp-wrap-with-pair "(")) "wrap")
  ("W" sp-unwrap-sexp)

  ;; Movement
  ("l" sp-next-sexp)
  ("h" sp-backward-sexp)
  ("j" sp-down-sexp)
  ("k" sp-backward-up-sexp)

  ("L" sp-forward-symbol)
  ("H" sp-backward-symbol)

  ("^" sp-beginning-of-sexp)
  ("$" sp-end-of-sexp)

  ("t" sp-transpose-sexp "transpose")
  ("u" undo-tree-undo "undo")

  ("y" sp-copy-sexp "copy")
  ("d" sp-kill-sexp "delete")

  ("s" sp-forward-slurp-sexp "slurp")
  ("S" sp-backward-slurp-sexp)

  ("b" sp-forward-barf-sexp "barf")
  ("B" sp-backward-barf-sexp)

  ("v" sp-select-next-thing "select")
  ("V" sp-select-previous-thing))

flycheck-errors

(defhydra flycheck-errors ()
  "Flycheck"
  ("q" nil)

  ;; Wrapping
  ("n" flycheck-next-error "next error")
  ("p" flycheck-previous-error "previous error"))

IBuffer

(setq ibuffer-saved-filter-groups
      '(("HFJ"
         ("org" (mode . org-mode))
         ("slime" (or
                     (name . "^\\*inferior-lisp")
                     (name . "^\\*slime-")))
         ("programming"
          (or (derived-mode . prog-mode)
              (mode         . ess-mode)
              (mode         . compilation-mode)))
         ("text document"
          (and (derived-mode . text-mode)
               (not (starred-name))))
         ("web"
          (or (derived-mode . sgml-mode)
              (derived-mode . css-mode)
              (mode         . javascript-mode)
              (mode         . js2-mode)
              (mode         . scss-mode)
              (derived-mode . haml-mode)
              (mode         . sass-mode)))
         ("dired" (mode . dired-mode))
         ("planner" (or
                     (name . "^\\*Calendar\\*$")
                     (name . "^diary$")))
         ("magit" (name . "^magit[-:]")))))

(defun hfj/ibuffer-mode-hook ()
  (ibuffer-auto-mode 1)
  (ibuffer-switch-to-saved-filter-groups "HFJ")
  (add-to-list 'ibuffer-never-show-predicates "^\\*helm")

  ;; Use human readable Size column instead of original one
  (define-ibuffer-column size-h
    (:name "Size" :inline t)
    (let ((bs (buffer-size)))
      (cond
       ((> bs 1000000) (format "%7.1fM" (/ bs 1000000.0)))
       ((> bs 100000) (format "%7.0fk" (/ bs 1000.0)))
       ((> bs 1000) (format "%7.1fk" (/ bs 1000.0)))
       (t (format "%8d" bs)))))

  ;; Show persp mode layout name
  (define-ibuffer-column persp
    (:name "Tab" :inline t)
    (let* ((p (persp--buffer-in-persps (current-buffer))))
      (if (null p)
          ""
        (persp-name (car p)))))

  ;; Modify the default ibuffer-formats
  (setq ibuffer-formats
        '((mark modified read-only " "
                (name 18 18 :left :elide)
                " "
                (size-h 9 -1 :right)
                " "
                (mode 16 16 :left :elide)
                " "
                (persp 10 10 :left :elide)
                " "
                filename-and-process))))

(add-hook 'ibuffer-mode-hook 'hfj/ibuffer-mode-hook)

Indentation

(use-package aggressive-indent
  :commands aggressive-indent-mode)

Itail

(use-package itail
  :commands itail)

Key chords

(use-package key-chord
  :demand t
  :after evil
  :config
  (key-chord-mode 1))

Layouts (persp-mode)

(use-package persp-mode
  :demand t
  :config
  (setq persp-auto-resume-time -1 ;; No autoload buffers
        persp-set-last-persp-for-new-frames nil
        persp-reset-windows-on-nil-window-conf t
        persp-autokill-buffer-on-remove t
        persp-add-buffer-on-after-change-major-mode t
        persp-kill-foreign-buffer-behaviour 'kill)
  (persp-mode 1)
  ;; (define-key evil-normal-state-map (kbd "gt") 'persp-next)
  ;; (define-key evil-normal-state-map (kbd "gT") 'persp-prev)
  (with-eval-after-load "term"
    (persp-def-auto-persp "term"
                          :parameters '((dont-save-to-file . t))
                          :mode 'term-mode
                          :dyn-env '(after-switch-to-buffer-functions ;; prevent recursion
                                     (persp-add-buffer-on-find-file nil)
                                     persp-add-buffer-on-after-change-major-mode)
                          :hooks '(after-switch-to-buffer-functions)
                          :switch 'window)))

Org

(defun hfj/org-mode-hook ()
  (company-mode t)
  ;; Incorporate local completions into company
  (add-hook 'completion-at-point-functions 'pcomplete-completions-at-point nil t))

(use-package org
  :mode (("\\.org$" . org-mode))
  :ensure org-plus-contrib
  :config
  (setq org-hide-leading-stars t
        org-edit-src-persistent-message nil
        org-imenu-depth 6
        org-startup-indented t)
  (add-hook 'org-mode-hook 'hfj/org-mode-hook)
  (hfj/leader-bind 'org-mode-map "o" 'org-open-at-point "open link")
  (hfj/leader-bind 'org-mode-map "'" 'org-edit-special "source edit")
  (hfj/leader-bind 'org-mode-map "i" 'org-indent-mode "indent")
  (hfj/leader-bind 'org-mode-map "z" nil "source block")
  (hfj/leader-bind 'org-mode-map "z z" 'hfj-org-mode/yank-org-src-block "copy block")
  (hfj/minor-leader-bind 'org-src-mode "c" 'org-edit-src-exit "save")
  (hfj/minor-leader-bind 'org-src-mode "k" 'org-edit-src-abort "cancel")
  (general-define-key :states 'normal
                      :keymaps 'org-mode-map
                      "<return>" 'org-open-at-point))

;; For org html export
(use-package htmlize)

(use-package evil-org
  :commands evil-org-mode
  :after org
  :init
  (add-hook 'org-mode-hook 'evil-org-mode)
  :config
  (add-hook 'evil-org-mode-hook
            (lambda ()
              (evil-org-set-key-theme '(textobjects insert navigation additional todo heading)))))

Org Agenda

(defun hfj/maybe-save-org-files ()
  (save-some-buffers nil #'(lambda () (org-agenda-file-p))))

(defun hfj/org-agenda-goto ()
  (interactive)
  (org-agenda-goto t)
  (evil-scroll-line-to-top nil))

(use-package org-agenda
  :commands org-agenda
  :ensure nil
  :after org
  :config
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys)
  (require 'org-habit)
  (setq org-habit-graph-column 54)
  (require 'org-super-agenda)
  (general-define-key :states 'motion
                      :keymaps 'org-agenda-mode-map
                      "<return>" 'hfj/org-agenda-goto)
  (advice-add 'org-agenda-quit :before 'hfj/maybe-save-org-files)
  (setq org-agenda-span 2)
  (setq org-agenda-custom-commands
        '(("g" "By group"
           ((agenda "" ((org-super-agenda-groups
                         '((:name "Timed" :time-grid t)
                           (:name "Grouped" :auto-group t)))))
            (todo "" ((org-agenda-overriding-header "Projects")
                      (org-super-agenda-groups
                       '((:name none  ; Disable super group header
                                :children todo)
                         (:discard (:anything t))))))))
          ("u" "unscheduled tasks" tags "-DEADLINE={.+}-SCHEDULED={.+}/!TODO|STARTED|WAITING"))))

(use-package org-super-agenda
  :after org
  :config
  (org-super-agenda-mode))

Org Alert

(use-package org-alert
  :load-path "local-packages/org-alert/"
  :after org
  :commands (org-alert-check org-alert-enable)
  :init
  (hfj/after-config
   (setq ;; org-alert-headline-regexp "\\(Sched[^:]+:[^:\n]+\\|TODO [^:!\n]+\\|Deadline:[^:\n]+\\)"
         org-alert-notification-title "gtd"
         org-alert-interval (* 60 20))
   (org-alert-enable)))

Org Bullets

(use-package org-bullets
    :after org
    :commands org-bullets-mode
    :init
    (add-hook 'org-mode-hook 'org-bullets-mode))

Org Drill

(use-package org-drill
  :commands (org-drill org-drill-cram org-drill-tree)
  :after org
  :ensure nil
  :init
  (hfj/leader-declare-subtree 'org-mode-map "d" "org-drill")
  (hfj/leader-bind 'org-mode-map "d d" 'org-drill-tree "drill tree at point")
  (hfj/leader-bind 'org-mode-map "d f" 'org-drill "drill file")
  (hfj/leader-bind 'org-mode-map "d c" 'org-drill-cram "cram")
  (hfj/leader-bind 'org-mode-map "d r" 'org-drill-resume "resume")

  (hfj/leader-declare-subtree 'org-mode-map "d a" "add")
  (hfj/leader-bind 'org-mode-map "d a q" 'hfj/drill-add-q-and-a "Q/A")
  (hfj/leader-bind 'org-mode-map "d a 2" 'hfj/drill-add-two-sided "2 sided")
  (hfj/leader-bind 'org-mode-map "d a c" 'hfj/drill-add-cloze "cloze")
  (hfj/leader-bind 'org-mode-map "d a m" 'hfj/drill-add-multi-sided "multi sided"))

(defun hfj/drill-add-q-and-a ()
  "Add a question and answer org-drill as subheading of current section."
  (interactive)
  ;; Add new subheading
  (move-end-of-line nil)
  (org-insert-subheading nil)
  (save-excursion
    (insert "Q/A-Card")
    (org-set-tags-to (list "drill"))
    (org-set-tags nil t)  ;; Align tag
    (move-end-of-line nil)
    (insert "\nTODO: question\n")

    ;; Add answer subheading
    (move-end-of-line nil)
    (org-insert-subheading nil)
    (insert "The Answer")
    (insert "\nTODO: answer")))

(defun hfj/drill-add-two-sided ()
  "Add a two-sided org-drill as subheading of current section."
  (interactive)
  ;; Add new subheading
  (move-end-of-line nil)
  (org-insert-subheading nil)
  (save-excursion
    (insert "Two-Sided-Card")
    (org-set-tags-to (list "drill"))
    (org-set-tags nil t)  ;; Align tag
    (move-end-of-line nil)
    (insert "
:PROPERTIES:
:DRILL_CARD_TYPE: twosided
:END:
TODO: Always shown header
")

    ;; Add card 1
    (org-insert-subheading nil)
    (insert "Side-1")
    (insert "\nTODO: side 1")

    ;; Add card 2
    (org-insert-heading nil)
    (insert "Side-2")
    (insert "\nTODO: side 2")

    ;; Add notes
    (org-insert-heading nil)
    (insert "Notes")
    (insert "\nTODO: notes and examples")
    ))

(defun hfj/drill-add-multi-sided ()
  "Add a multi-sided org-drill as subheading of current section."
  (interactive)
  ;; Add new subheading
  (move-end-of-line nil)
  (org-insert-subheading nil)
  (save-excursion
    (insert "Two-Sided-Card")
    (org-set-tags-to (list "drill"))
    (org-set-tags nil t)  ;; Align tag
    (move-end-of-line nil)
    (insert "
:PROPERTIES:
:DRILL_CARD_TYPE: multisided
:END:
TODO: Always shown header
")

    ;; Add card 1
    (org-insert-subheading nil)
    (insert "Side-1")
    (insert "\nTODO: side 1")

    ;; Add card 2
    (org-insert-heading nil)
    (insert "Side-2")
    (insert "\nTODO: side 2")

    ;; Add card 3
    (org-insert-heading nil)
    (insert "Side-3")
    (insert "\nTODO: side 3")))

(defun hfj/drill-add-cloze ()
  "Add a multi-cloze org-drill as subheading of current section."
  (interactive)
  ;; Add new subheading
  (move-end-of-line nil)
  (org-insert-subheading nil)
  (save-excursion

    (insert "Cloze-Card")
    (org-set-tags-to (list "drill"))
    (org-set-tags nil t)  ;; Align tag
    (move-end-of-line nil)
    (insert "
:PROPERTIES:
:DRILL_CARD_TYPE: hide1cloze
:END:
Fill in [word] or [another|with hint]
")))

Org inside comments (poporg)

(use-package poporg
  :after org)

Org Journal

(use-package org-journal
  :after org
  :config
  (setq org-journal-dir "~/Documents/journal/"
        org-journal-file-format "%Y/%Y-%m/%Y-%m-%d.org"
        org-journal-date-prefix "#+TITLE: "
        org-journal-date-format "%A, %B %d %Y"
        org-journal-time-prefix "* "
        org-journal-time-format "%R "
        org-journal-find-file 'find-file))

Org Web Mode

(use-package org-web-tools
  :after org
  :commands (org-web-tools-read-url-as-org
             org-web-tools-convert-links-to-page-entries
             org-web-tools-insert-link-for-url
             org-web-tools-insert-web-page-as-entry))

Outliner

(use-package outshine
  :init
  (add-hook 'outline-minor-mode-hook 'outshine-hook-function)
  (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
  (setq outshine-use-speed-commands t))

Parenthesis

Smartparens: Add closing braces automatically

(use-package smartparens
  :commands smartparens-mode
  :diminish smartparens-mode)
(use-package evil-smartparens
  :commands evil-smartparens-mode
  :init
  (add-hook 'smartparens-enabled-hook 'evil-smartparens-mode))

Rainbow delimiters

(use-package rainbow-delimiters
  :commands rainbow-delimiters-mode
  :init
  (add-hook 'lisp-mode-hook 'rainbow-delimiters-mode)
  (add-hook 'emacs-lisp-mode-hook 'rainbow-delimiters-mode))

PDF

(use-package pdf-tools
  :defer 1
  :config
  ;; initialize
  (pdf-tools-install)
  ;; open pdfs scaled to fit page
  (setq-default pdf-view-display-size 'fit-page)
  ;; automatically annotate highlights
  (setq pdf-annot-activate-created-annotations t
        pdf-view-resize-factor 1.05)
  (general-define-key :states 'normal
                      :keymaps 'pdf-view-mode-map
                      "o" 'pdf-outline))

Proced

(setf proced-format 'long)

Project Management

(use-package projectile
  :demand t
  :config
  ;; Without this, projectile spends too much time trying to figure out the mode
  ;; line.
  (setq projectile-mode-line "Projectile")
  (require 'helm-projectile)
  (projectile-global-mode))
(use-package helm-projectile
  :after projectile
  :config
  (setq projectile-enable-caching t
        projectile-indexing-method 'alien))

Purpose

(use-package window-purpose
  :config
  (purpose-mode))

Recent Files

(use-package recentf
  :ensure nil
  :config
  (recentf-mode 1)
  (setq recentf-max-menu-items 100)
  (run-at-time nil (* 60 60) 'recentf-save-list))

S for strings

(use-package s
  :demand t)

Save file position

(save-place-mode 1)
;; Give up if file is no longer available
(setq save-place-forget-unreadable-files nil)

Search

(use-package ag
  :demand t)

Scratch

(defvar hfj/open-layer-scratch-directory (concat user-emacs-directory
                                                 "/.cache/scratch"))

(defun hfj/open-layer-scratch-path (&optional layout-name)
  "Determine the path for the scratch for the current layout, or the one supplied.  Path will be in hfj/open-layer-scratch-directory."
  (let* ((layout-name (or layout-name (safe-persp-name (get-frame-persp)) "default"))
         (file-name (replace-regexp-in-string "[\\\\/ ]" "-" layout-name)))
    (concat hfj/open-layer-scratch-directory
            "/"
            (hfj/dash-escape-file-name (downcase layout-name))
            ".org")))

(defun hfj/open-layer-scratch ()
  "Open a scratch pad for the current layout.  Files are stored in hfj/open-layer-scratch-directory."
  (interactive)
  (let ((layout-name (or (safe-persp-name (get-frame-persp)) "default")))
    (cond ((string= layout-name persp-nil-name)
           (switch-to-buffer "*scratch*"))
          (t
           (let ((scratch-path (hfj/open-layer-scratch-path layout-name)))
             (find-file scratch-path)
             (when (= (point-min) (point-max))
               (insert (concat "#+TITLE: Scratch: " (capitalize layout-name) "\n"))))))))

Roku

(defun roku-key-to-command (key)
  (cond ((eq key 'home) "Home")
        ((eq key 'reverse) "Rev")
        ((eq key 'forward) "Fwd")
        ((eq key 'play) "Play")
        ((eq key 'select) "Select")
        ((eq key 'left) "Left")
        ((eq key 'right) "Right")
        ((eq key 'down) "Down")
        ((eq key 'up) "Up")
        ((eq key 'back) "Back")
        ((eq key 'instant-replay) "InstantReplay")
        ((eq key 'info) "Info")
        ((eq key 'backspace) "Backspace")
        ((eq key 'search) "Search")
        ((eq key 'enter) "Enter")
        (t (url-hexify-string (format "Lit_%c" key)))))

(defun roku-make-keypress-url (host key)
  (concat "http://" host ":8060/keypress/" (roku-key-to-command key)))

(defun roku-callback (x)
  (when x
    (message "%s" x)))

(defun roku-send-keypress (host key)
  (let ((url-request-method "POST")
        (url-request-data ""))
    (url-retrieve (roku-make-keypress-url host key) #'roku-callback)))

;;; Tied to host

(defvar roku-current-host nil)

(defun roku-run (host)
  "Initiate transient state."
  (setq roku-current-host host)
  (hydra-roku/body))

(defmacro roku-make-command (command)
  `(defun ,(intern (concat "roku-current-send-" (symbol-name command))) ()
     (interactive)
     (roku-send-keypress roku-current-host ',command)))

(roku-make-command back)
(roku-make-command home)
(roku-make-command info)
(roku-make-command instant-replay)

(roku-make-command play)
(roku-make-command search)
(roku-make-command select)

(roku-make-command backspace)
(roku-make-command enter)

(roku-make-command forward)
(roku-make-command reverse)

(roku-make-command up)
(roku-make-command down)
(roku-make-command left)
(roku-make-command right)

(defun roku-send-text-sender (str-to-send pos)
  (let ((url-request-method "POST")
        (url-request-data ""))
    (cond ((< pos (length str-to-send))
           (let ((url (roku-make-keypress-url roku-current-host (elt str-to-send pos))))
             (url-retrieve url
                           #'(lambda (status)
                               (roku-send-text-sender str-to-send (1+ pos))))))
          (t
           (roku-current-send-enter)))))

(defun roku-send-text ()
  (interactive)
  (let ((s (read-string "Send text: ")))
    (when (stringp s)
      (message "%s" s)
      (roku-send-text-sender s 0))
    t))

(defhydra hydra-roku (:hint nil)
  "
[_h_] left [_j_] down [_k_] up [_l_] right
[_<return>_] ok [_SPC_] play [_gh_] home [_<backspace>_] backspace
[_H_] back [_i_] info [_m_] instant replay [_s_] search
[_,_] reverse [_._] forward [_t_] enter text [_q_] quit"
  ("q" nil)

  ("h" roku-current-send-left)
  ("l" roku-current-send-right)
  ("j" roku-current-send-down)
  ("k" roku-current-send-up)

  ("<backspace>" roku-current-send-backspace)

  ("H" roku-current-send-back)

  ("<return>" roku-current-send-select)
  ("SPC" roku-current-send-play)

  ("gh" roku-current-send-home)
  ("i" roku-current-send-info)
  ("m" roku-current-send-instant-replay)
  ("s" roku-current-send-search)

  ("," roku-current-send-reverse)
  ("." roku-current-send-forward)
  ("t" roku-send-text))

Text

(defun hfj/text-mode-hook ()
  )

(add-hook 'text-mode-hook 'hfj/text-mode-hook)

Tilde Fringe

(use-package vi-tilde-fringe
  :demand t
  :config
  (global-vi-tilde-fringe-mode))

Toggles Hydra

(defun hfj/y-n (val)
  (if val "y" "n"))

(defun hfj/toggle-line-numbers ()
  (interactive)
  (cond ((version< emacs-version "26") nil)
        ((or (not (boundp 'display-line-numbers))
             (null display-line-numbers))
         (display-line-numbers-mode))
        ((eq display-line-numbers t)
         (setq display-line-numbers 'relative))
        ((eq display-line-numbers 'relative)
         (setq display-line-numbers 'visual))
        (t
         (setq display-line-numbers nil))))

(defun hfj/toggle-super-sub-words ()
  (interactive)
  (cond (subword-mode
         (subword-mode 0)
         (superword-mode 1))
        (superword-mode
         (subword-mode 0)
         (superword-mode 0))
        (t
         (subword-mode 1))))

(defun hfj/org-alert-timer-enabled-p ()
  (let ((has-timer nil))
    (dolist (timer timer-list)
      (when (eq (elt timer 5) 'org-alert-check)
        (setq has-timer t)))
    has-timer))

(defun hfj/toggle-org-alert ()
  (interactive)
  (if (hfj/org-alert-timer-enabled-p)
      (org-alert-disable)
    (org-alert-enable)))

(defvar hfj/hydra-toggles
  `((?a "_a_ autofill %s(hfj/ht ?a)"
        ,#'(lambda () (hfj/y-n auto-fill-function)))
    (?c "_c_ centered %s(hfj/ht ?c)"
        ,#'(lambda () (hfj/y-n (and (boundp 'centered-cursor-mode) centered-cursor-mode))))
    (?i "_i_ icase search %s(hfj/ht ?i)"
        ,#'(lambda () (hfj/y-n case-fold-search)))
    (?I "_I_ agressive indent %s(hfj/ht ?I)"
        ,#'(lambda () (hfj/y-n aggressive-indent-mode)))
    (?n "_n_ line-numbers %s(hfj/ht ?n)"
        ,#'(lambda () (cond ((version< emacs-version "26") nil)
                       ((or (not (boundp 'display-line-numbers))
                            (null display-line-numbers))
                        "n")
                       ((eq display-line-numbers t)
                        "a")
                       ((eq display-line-numbers 'relative)
                        "r")
                       (t
                        "v"))))
    (?o "_o_ org-alert %s(hfj/ht ?o)"
        ,#'(lambda () (hfj/y-n (hfj/org-alert-timer-enabled-p))))
    (?s "_s_ sup/sub words %s(hfj/ht ?s)"
        ,#'(lambda () (cond (subword-mode "-")
                       (superword-mode "+")
                       (t "n"))))
    (?t "_t_ truncate-lines %s(hfj/ht ?t)"
        ,#'(lambda () (hfj/y-n truncate-lines)))
    (?T "_T_ toggle-tabs %s(hfj/ht ?T)"
        ,#'(lambda () (hfj/y-n indent-tabs-mode)))
    (?w "_w_ wrap-lines %s(hfj/ht ?w)"
        ,#'(lambda () (hfj/y-n word-wrap)))
    (?W "_W_ whitespace %s(hfj/ht ?W)"
        ,#'(lambda () (hfj/y-n (cond ((local-variable-p 'whitespace-mode)
                                 whitespace-mode)
                                (t
                                 global-whitespace-mode)))))))

(defun hfj/ht (key)
  (let ((row (assoc key hfj/hydra-toggles)))
    (when row
      (funcall (elt row 2)))))

(defun hfj/toggle-case-fold-search ()
  (interactive)
  (setq case-fold-search (not case-fold-search)))

(defun hfj/toggle-whitespace-mode ()
  (interactive)
  (cond ((local-variable-p 'whitespace-mode)
         (whitespace-mode (if whitespace-mode 0 1)))
        (t
         (whitespace-mode (if global-whitespace-mode 0 1)))))

(defun hfj/toggle-toggle-tabs ()
  (interactive)
  (setq-local indent-tabs-mode (not indent-tabs-mode)))

(defhydra hydra-toggles (:hint nil)
  (format "%s" (hfj/dynamic-format-hydra-doc "Toggles"
                                             (map 'list #'(lambda (row) (elt row 1)) hfj/hydra-toggles)))
  ("a" auto-fill-mode)
  ("c" centered-cursor-mode)
  ("i" hfj/toggle-case-fold-search)
  ("I" aggressive-indent-mode)
  ("n" hfj/toggle-line-numbers)
  ("o" hfj/toggle-org-alert)
  ("s" hfj/toggle-super-sub-words)
  ("t" toggle-truncate-lines)
  ("T" hfj/toggle-toggle-tabs)
  ("w" visual-line-mode)
  ("W" hfj/toggle-whitespace-mode)
  ("q" nil))

Tramp

(let ((cache-seconds (* 60 60 4)))
  (setq
   ;; Disable version control
   vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)" vc-ignore-dir-regexp tramp-file-name-regexp)
   ;; May be faster than default scp
   tramp-default-method "ssh"
   ;; Don't invalidate cache quickly
   remote-file-name-inhibit-cache cache-seconds
   ;; speed up completions
   tramp-completion-reread-directory-timeout cache-seconds
   ;; How long to cache entered passwords
   password-cache-expiry 900))

Undo

(use-package undo-tree
  :config
  (global-undo-tree-mode -1))

Version Control

Magit

(use-package magit
  :commands (magit-status)
  :config
  (require 'evil-magit)
  (setq magit-ediff-dwim-show-on-hunks t))
(use-package evil-magit)

Git gutter

(use-package git-gutter
  :commands (git-gutter-mode git-gutter:previous-hunk git-gutter:next-hunk)
  :init
  (add-hook 'org-mode-hook 'git-gutter-mode)
  (add-hook 'prog-mode-hook 'git-gutter-mode)
  :diminish git-gutter-mode)
(defun hfj/git-gutter-prev-hunk (arg)
  (interactive "p")
  (git-gutter:previous-hunk arg)
  (evil-scroll-line-to-center nil))
(defun hfj/git-gutter-next-hunk (arg)
  (interactive "p")
  (git-gutter:next-hunk arg)
  (evil-scroll-line-to-center nil))

vc-msg

(use-package vc-msg
  :commands (vc-msg-show))

Wiki

(defun hfj/helm-multidir-select-org-file (dirs helm-name)
  "Use helm to select a single org file from among a list of directories."
  (let (results)
    (map nil
         (lambda (dir-item)
           (let (dirpath description)
             (cond ((stringp dir-item) (setf dirpath dir-item))
                   ((consp dir-item) (setf dirpath (cdr dir-item) description (car dir-item)))
                   (t (error "Unknown dir item type: %s" dir-item)))
             (map nil
                  (lambda (filepath)
                    (let* ((filename (substring (file-name-nondirectory filepath) 0 -4))
                           (name (cond ((stringp description) (format "%s (%s)" filename description))
                                       (t filename))))
                      (push (cons name filepath) results)))
                  (directory-files dirpath t "^[a-zA-Z0-9].*\.org$"))))
         dirs)
    (setq results (sort results (lambda (a b) (string< (car a) (car b)))))
    (helm :sources (helm-build-sync-source helm-name
                     :candidates results))))

(defun hfj/helm-multidir-find-org-file (dirs helm-name)
  (interactive)
  (let ((selected-filepath (hfj/helm-multidir-select-org-file dirs helm-name)))
    (when selected-filepath
      (find-file selected-filepath))))

Windows

Buffer Alist

(defun hfj/buffer-mode (buffer-or-name)
  (with-current-buffer buffer-or-name major-mode))

(defun hfj/make-mode-matcher (mode)
  #'(lambda (buffer-name action)
      (let ((result (eql mode (hfj/buffer-mode buffer-name))))
        ;; (message "Result of %s: %s" buffer-name result)
        result)))

(add-to-list 'display-buffer-alist
             `(,(rx bos "*Flycheck errors*" eos)
               display-buffer-in-side-window
               (side . bottom)
               (slot . 1)
               (window-height   . 12)
               (preserve-size . (nil . t))
               (window-parameters . ((no-other-window . t)
                                     (no-delete-other-windows . t)))))

(add-to-list 'display-buffer-alist
             `(,(rx bos "*Flycheck error messages*" eos)
               display-buffer-in-side-window
               (side . bottom)
               (slot . -1)
               (window-height   . 12)
               (preserve-size . (nil . t))
               (window-parameters . ((no-other-window . t)
                                     (no-delete-other-windows . t)))))

(add-to-list 'display-buffer-alist
             `(,(rx bos "*Warnings*" eos)
               display-buffer-in-side-window
               (side . top)
               (slot . 0)
               (window-height . fit-window-to-buffer)
               (preserve-size . (nil . t))
               (window-parameters . ((no-other-window . t)
                                     (no-delete-other-windows . t)))))

(add-to-list 'display-buffer-alist
             `(,(rx bos "magit: ")
               (display-buffer-reuse-window display-buffer-in-previous-window display-buffer-same-window)))

(add-to-list 'display-buffer-alist
             `(,(rx bos "magit-revision: ")
               (display-buffer-same-window)))

(add-to-list 'display-buffer-alist
             `(,(rx bos "*slime-scratch*")
               (display-buffer-reuse-window display-buffer-same-window)))

(add-to-list 'display-buffer-alist
             `(,(rx bos "*slime-repl")
               (display-buffer-reuse-window display-buffer-same-window)))

;; (defun hfj/match-dired-on-non-dired (buffer-name action)
;;   (and (eq 'dired-mode (hfj/buffer-mode buffer-name))
;;        (not (eq 'dired-mode major-mode))))

;; ;; Dired to popup
;; (add-to-list 'display-buffer-alist
;;              '(hfj/match-dired-on-non-dired
;;                (display-buffer-pop-up-window display-buffer-in-side-window)
;;                (side            . bottom)))

Set height by selection

(defun hfj/set-window-height-to-region (pos1 pos2)
  "Take the selected region and resize the current window to it."
  (interactive "r")
  (set-window-text-height nil (count-lines pos1 pos2))
  (deactivate-mark t)
  (setf (point) pos1)
  (evil-scroll-line-to-top nil))

Swap buffers by direction

(use-package buffer-move
  :defer 1
  :commands (buf-move-left buf-move-right buf-move-up buf-move-down))

Toggle window dedicated

(defun hfj/toggle-window-dedicated ()
  "Dedicate the current buffer to the current window."
  (interactive)
  (let* ((window (get-buffer-window (current-buffer)))
         (initially-dedicated (window-dedicated-p window)))
    (set-window-dedicated-p window (not initially-dedicated))
    (if initially-dedicated
        (message "Window dedication removed")
      (message "Window dedication set"))))

Indirect buffers

(defun hfj/clone-indirect-buffer-here ()
  "Create an indirect buffer at the current window, cloning the current buffer."
  (interactive)
  (let* ((prefix "View:")
         (real-buffer-name (replace-regexp-in-string "<[0-9]+>" "" (s-chop-prefix prefix (buffer-name))))
         (window-to-use (selected-window))
         (buf (make-indirect-buffer real-buffer-name
                                    (generate-new-buffer-name (concat prefix real-buffer-name))
                                    t)))
    (set-window-buffer window-to-use buf)))

Undo window changes

(use-package winner
  :demand t
  :config (winner-mode 1))

Whitespace

(setq whitespace-style
      '(face
        tabs
        ;; spaces  ; highlight spaces
        trailing ; highlight spaces at end of line(should be eliminated on save)
        ;; lines  ; Highlight long lines
        ;; lines-tail  ; Highlight long lines only after the column
        space-before-tab
        newline
        indentation
        ;; empty
        space-after-tab
        ;; space-mark  ; Place . in every space
        tab-mark ; Show all TABs
        ;; newline-mark  ; $ at end of line
        ))

(setq whitespace-display-mappings
      '((space-mark 32 [183] [46]) ; normal space
        (space-mark 160 [164] [95])
        (space-mark 2208 [2212] [95])
        (space-mark 2336 [2340] [95])
        (space-mark 3616 [3620] [95])
        (space-mark 3872 [3876] [95])
        (newline-mark 10 [182 10]) ; newlne
        (tab-mark 9 [9655 9] [92 9]) ; tab
        ))

(add-hook 'after-init-hook (lambda ()
                             (global-whitespace-mode 1)))

YASnippet

(defvar smartparens-mode-original-value)

(defun disable-sp-hippie-advice (&rest _)
  (setq smartparens-mode-original-value smartparens-mode)
  (setq smartparens-mode nil)
  t) ; We should still return t.
;; This advice could be added to other functions that usually insert
;; balanced parens, like `try-expand-list'.
(advice-add 'yas-hippie-try-expand :after-while #'disable-sp-hippie-advice)

(defun reenable-sp-hippie-advice (&rest _)
  (when (boundp 'smartparens-mode-original-value)
    (setq smartparens-mode smartparens-mode-original-value)
    (makunbound 'smartparens-mode-original-value)))

(use-package yasnippet
  :demand t
  :config
  (yas-global-mode 1)
  (require 'hippie-exp)
  (setq hippie-expand-try-functions-list
        (cons 'yas-hippie-try-expand hippie-expand-try-functions-list))

  (define-key yas-minor-mode-map (kbd "<tab>") nil)
  (define-key yas-minor-mode-map (kbd "TAB") nil)
  ;; (define-key yas-minor-mode-map (kbd "<the new key>") yas-maybe-expand) ; use hippie

  ;;keys for navigation
  (define-key yas-keymap [(tab)]       nil)
  (define-key yas-keymap (kbd "TAB")   nil)
  (define-key yas-keymap [(shift tab)] nil)
  (define-key yas-keymap [backtab]     nil)
  (define-key yas-keymap (kbd "M-n") 'yas-next-field)
  (define-key yas-keymap (kbd "M-p") 'yas-prev-field)

  (advice-add 'hippie-expand :after #'reenable-sp-hippie-advice
              ;; Set negative depth to make sure we go after
              ;; `sp-auto-complete-advice'.
              '((depth . -100))))

Bindings

Common

(define-key evil-insert-state-map (kbd "M-/") 'hippie-expand)
(hfj/spc-bind hfj/main-leader-key 'execute-extended-command "elisp cmd")

(key-chord-define evil-normal-state-map "op" 'helm-show-kill-ring)

(defun hfj/word-search (&optional backwards)
  (interactive)
  (er/mark-symbol)
  (let* ((forward (not backwards))
         (str (buffer-substring (region-beginning) (region-end)))
         (str-quoted (regexp-quote str))
         (re-str (concat (if (string= "w" (string (char-syntax (elt str 0))))
                             "\\b"
                           "")
                         str-quoted
                         (if (string= "w" (string (char-syntax (elt str (1- (length str))))))
                             "\\b"
                           ""))))
    (deactivate-mark t)
    (setq isearch-forward forward)
    (setq evil-regexp-search t)
    (push re-str regexp-search-ring)
    (evil-search re-str forward t)))

(defun hfj/word-search-backwards ()
  (interactive)
  (hfj/word-search t))

(define-key evil-normal-state-map (kbd "*") 'hfj/word-search)
(define-key evil-normal-state-map (kbd "#") 'hfj/word-search-backwards)

(define-key evil-motion-state-map (kbd "zf") 'reposition-window)

(define-key evil-motion-state-map (kbd "<f1>")
  #'(lambda ()
      (interactive)
      (org-agenda nil "g")
      (delete-other-windows)
      (let ((first-window (get-buffer-window)))
        (split-window-right)
        (org-agenda-redo)
        (windmove-right)
        (if (< 0 (length org-agenda-files))
            (find-file (car org-agenda-files))
          (switch-to-buffer "*scratch*"))
        (select-window first-window))))

(define-key evil-motion-state-map (kbd "<f2>")
  #'(lambda ()
      (interactive)
      (org-agenda nil "u")
      (delete-other-windows)
      (let ((first-window (get-buffer-window)))
        (split-window-right)
        (org-agenda-redo)
        (windmove-right)
        (if (< 0 (length org-agenda-files))
            (find-file (car org-agenda-files))
          (switch-to-buffer "*scratch*"))
        (select-window first-window))))

applications: SPC a

(hfj/spc-declare-subtree "a" "applications")
(hfj/spc-bind "a a" 'annotate-annotate "annotate")
(hfj/spc-bind "a A" #'(lambda ()
                        (interactive)
                        (unless (and (boundp 'annotate-mode)
                                     annotate-mode)
                          (annotate-mode)))
              "load annotations")

(hfj/spc-bind "a u" 'undo-tree-visualize "undo")
(hfj/spc-bind "a t" 'hfj/message-at-time "timer")
(hfj/spc-bind "a d" 'dired "dired")
(hfj/spc-bind "a p" 'proced "proced")

(hfj/spc-declare-subtree "a s" "shell")
(hfj/spc-bind "a s e" 'hfj/switch-to-eshell "eshell")
(hfj/spc-bind "a s E" 'hfj/eshell-here "new eshell here")
(hfj/spc-bind "a s s" 'hfj/switch-to-shell "shell")
(hfj/spc-bind "a s t" 'hfj/ansi-term-here "term")

(hfj/spc-declare-subtree "a r" "radio")
(hfj/spc-bind "a r c"
              (hfj/make-link-player "http://hpm.streamguys1.com/classical-aac.m3u")
              "houston classical")

wiki: SPC a w

(add-hook 'hfj/post-config-hook
          #'(lambda ()
              (when (and (boundp 'hfj/wiki-dirs)
                         (listp hfj/wiki-dirs))

                (defun hfj/pick-wiki-file ()
                  (interactive)
                  (hfj/helm-multidir-find-org-file hfj/wiki-dirs "wiki"))

                (defun hfj/pick-wiki-dir ()
                  (interactive)
                  (let ((selection (helm :sources (helm-build-sync-source "wiki dir"
                                                    :candidates hfj/wiki-dirs))))
                    (when selection
                      (dired selection))))

                (defun hfj/search-wiki ()
                  (interactive)
                  (let ((just-dirs (map 'list
                                        #'(lambda (x)
                                            (cond ((stringp x) x)
                                                  ((and (consp x) (stringp (cdr x))) (cdr x))))
                                        hfj/wiki-dirs)))
                    (with-helm-default-directory (car just-dirs)
                        (helm-do-ag nil just-dirs))))

                (hfj/spc-declare-subtree "a w" "wiki")
                (hfj/spc-bind "a w w" 'hfj/pick-wiki-file "find file")
                (hfj/spc-bind "a w d" 'hfj/pick-wiki-dir "dired")
                (hfj/spc-bind "a w a" 'hfj/search-wiki "search"))))

buffers: SPC b

(hfj/spc-declare-subtree "b" "buffers")
(hfj/spc-bind "b b" #'(lambda () (interactive) (helm-mini)) "buffers")

(defun hfj/delete-buffer ()
  "Delete current buffer without breaking layout"
  (interactive)
  (cond ((null (window-prev-buffers))
         (evil-delete-buffer (current-buffer)))
        (t
         (let ((window-to-use (selected-window))
               (config (current-window-configuration))
               (prev-buffer (save-excursion (previous-buffer))))
           (unwind-protect
               (kill-buffer (current-buffer))
             (set-window-configuration config))
           (set-window-buffer window-to-use prev-buffer)))))

(hfj/spc-bind "b d" 'hfj/delete-buffer "delete")

(defun hfj/slime-scratch ()
  "Create and switch to slime scratch."
  (interactive)
  (let* ((name "*slime-scratch*")
         (buf (get-buffer name)))
    (cond ((null buf)
           (save-window-excursion
             (slime-scratch))
           (pop-to-buffer name))
          (t
           (pop-to-buffer name)))))

(hfj/spc-bind "b l" 'hfj/slime-scratch "cl-scratch")
(hfj/spc-bind "b L" 'slime-repl "cl-repl")

(hfj/spc-bind "b i" #'ibuffer "ibuffer")
(hfj/spc-bind "b n" 'switch-to-next-buffer "next buffer")
(hfj/spc-bind "b p" 'switch-to-prev-buffer "prev buffer")
(hfj/spc-bind "b TAB" 'evil-switch-to-windows-last-buffer "last buffer")

(hfj/spc-bind "b s"
              #'(lambda ()
                  (interactive)
                  (switch-to-buffer "*scratch*"))
              "scratch")

(hfj/spc-bind "b S"
              #'(lambda ()
                  (interactive)
                  (switch-to-buffer "*scratch*")
                  (delete-other-windows))
              "only scratch")

converting: SPC c

(hfj/spc-declare-subtree "c" "convert")
(hfj/spc-bind "c s" 'hfj/point-seconds-to-date "seconds->date")

commenting: SPC ;

(hfj/spc-bind ";" 'evilnc-comment-or-uncomment-lines "comment")

errors: SPC e

(hfj/spc-declare-subtree "e" "errors")
(hfj/spc-bind "e l" 'flycheck-list-errors "list errors")

(hfj/spc-bind "e n" #'(lambda () (interactive) (flycheck-next-error) (flycheck-errors/body)) "next error")
(hfj/spc-bind "e p" #'(lambda () (interactive) (flycheck-previous-error) (flycheck-errors/body)) "previous error")

files: SPC f

(hfj/spc-declare-subtree "f" "file")
(hfj/spc-bind "f f" 'find-file "find file")
(hfj/spc-bind "f s" 'save-buffer "save")
(hfj/spc-bind "f S" 'save-some-buffers "save all")
(hfj/spc-bind "f b" 'helm-bookmarks "bookmarks")

(hfj/spc-bind "f y" 'hfj/show-and-copy-buffer-filename "copy filename")
(hfj/spc-bind "f Y" 'hfj/show-and-copy-buffer-filename-and-line "copy filename and line")

(hfj/spc-declare-subtree "f e" "edit")
(hfj/spc-bind "f e d"
              #'(lambda ()
                  (interactive)
                  (find-file (expand-file-name "dotemacs.org"
                                               user-emacs-directory)))
              "dotemacs.org")
(hfj/spc-bind "f e l"
              #'(lambda ()
                  (interactive)
                  (find-file (expand-file-name "locals.org"
                                               user-emacs-directory)))
              "dotemacs.org")

git: SPC g

(hfj/spc-declare-subtree "g" "git")

(hfj/spc-bind "g b" 'magit-blame "blame")

(hfj/spc-bind "g s" 'magit-status "status")

(hfj/spc-bind "g g" 'hydra-git-gutter/body "gutter")

(hfj/spc-bind "g m" 'vc-msg-show "message")

(hfj/spc-bind "g n"
              #'(lambda (arg)
                  (interactive "p")
                  (hfj/git-gutter-next-hunk arg)
                  (hydra-git-gutter/body))
              ' "next hunk")

(hfj/spc-bind "g p"
              #'(lambda (arg)
                  (interactive "p")
                  (hfj/git-gutter-prev-hunk arg)
                  (hydra-git-gutter/body))
              ' "prev hunk")

(hfj/spc-declare-subtree "g f" "file")
(hfj/spc-bind "g f h" 'magit-log-buffer-file "history")

help: SPC h

(hfj/spc-declare-subtree "h" "help")

(hfj/spc-declare-subtree "h d" "describe")

(hfj/spc-bind "h h" 'helm-apropos "symbol")
(hfj/spc-bind "h d b" 'describe-bindings "bindings")
(hfj/spc-bind "h d f" 'describe-function "function")
(hfj/spc-bind "h d k" 'describe-key "key")
(hfj/spc-bind "h d m" 'describe-mode "mode")
(hfj/spc-bind "h d t" 'describe-theme "theme")
(hfj/spc-bind "h d v" 'describe-variable "variable")

(hfj/spc-bind "h i" 'info "info")

jumps: SPC j

(hfj/spc-declare-subtree "j" "jumps")
(hfj/spc-bind "j l"
              #'(lambda (&optional arg)
                  (interactive "p")
                  (let ((avy-all-windows nil))
                    (evil-avy-goto-line arg)))
              "jump to line")
(hfj/spc-bind "j p" 'avy-pop-mark "pop mark")
(hfj/spc-bind "j j" 'evil-avy-goto-char-timer "goto char")

layouts: SPC l

(hfj/spc-bind "l" 'hydra-persp/body "layouts")

narrow/number: SPC n

(hfj/spc-declare-subtree "n" "narrow/number")
(hfj/spc-bind "n f" 'narrow-to-defun "function")
(hfj/spc-bind "n n" 'widen "widen")
(hfj/spc-bind "n p" 'narrow-to-page "page")
(hfj/spc-bind "n r" 'narrow-to-region "region")
(hfj/spc-bind "n w" 'widen "widen")

(hfj/spc-bind "n +" 'evil-numbers/inc-at-pt "number+")
(hfj/spc-bind "n -" 'evil-numbers/dec-at-pt "number-")

(hfj/spc-bind-on-major 'org-mode-map "n b" 'org-narrow-to-block "org block")
(hfj/spc-bind-on-major 'org-mode-map "n e" 'org-narrow-to-element "org element")
(hfj/spc-bind-on-major 'org-mode-map "n s" 'org-narrow-to-subtree "org subtree")

org: SPC o

(hfj/spc-declare-subtree "o" "org")
(hfj/spc-declare-subtree "o a" "org agenda")
(hfj/spc-bind "o a a" 'org-agenda "agenda")
(hfj/spc-bind "o a g"
              #'(lambda ()
                  (interactive)
                  (org-agenda nil "g"))
              "by group")

(hfj/spc-declare-subtree "o C" "org clock")
(hfj/spc-bind "o C o" 'org-clock-clock-out "clock out")
(hfj/spc-bind "o C c" 'org-clock-clock-cancel "clock cancel")
(hfj/spc-bind "o C C" 'org-clock-goto "clock goto")

(hfj/spc-bind "o l" 'org-store-link "store link")
(hfj/spc-bind "o L" 'org-insert-link "insert link")
(hfj/spc-bind "o j j" 'org-journal-new-entry "new entry")
(hfj/spc-bind "o j p" 'org-journal-previous-entry "previous entry")
(hfj/spc-bind "o j n" 'org-journal-next-entry "next entry")

(hfj/spc-bind "o p" 'poporg-dwim "poporg")

(hfj/spc-bind "o <tab>" 'org-cycle "cycle")

outline: SPC O

(hfj/spc-bind "O"
              #'(lambda ()
                  (interactive)
                  (unless outline-minor-mode
                    (outline-minor-mode))
                  (hydra-outline/body))
              "outline")

project: SPC p

(require 'projectile)
(require 'helm-projectile)
(hfj/spc-declare-subtree "p" "projectile")
(hfj/spc-bind "p p" 'helm-projectile-switch-project "switch project")
(hfj/spc-bind "p f" 'helm-projectile-find-file "find file")
(hfj/spc-bind "p d" 'helm-projectile-find-dir "find dir")
(hfj/spc-bind "p I" 'projectile-invalidate-cache "invalidate cache")
(hfj/spc-bind "p r" 'projectile-replace "replace")
(hfj/spc-bind "p R" 'projectile-replace-regexp "replace regex")

quit: SPC q

(hfj/spc-declare-subtree "q" "quit")
(defun hfj/non-server-quit ()
  (interactive)
  (cond (multiple-frames (delete-frame))
        (t (save-buffers-kill-emacs))))
(cond ((daemonp)
       (hfj/spc-bind "q q" 'delete-frame "kill frame"))
      (t
       (hfj/spc-bind "q q" 'hfj/non-server-quit "quit")))
(hfj/spc-bind "q Q" 'save-buffers-kill-emacs "quit all")

search: SPC /

(defun hfj/quick-search ()
  (interactive)
  (cond ((ignore-errors (projectile-project-root))
         (helm-do-ag (projectile-project-root)))
        ((project-current)
         (helm-do-ag-project-root))
        (buffer-file-name
         (helm-do-ag (file-name-directory buffer-file-name)))
        (t
         (helm-do-ag))))

(defun hfj/quick-search-wap ()
  (interactive)
  (er/mark-symbol)
  (hfj/quick-search))

(hfj/spc-bind "/" 'hfj/quick-search "quick search")
(hfj/spc-bind "*" 'hfj/quick-search-wap "quick search wap")

search: SPC s

(hfj/spc-declare-subtree "s" "search")
(hfj/spc-bind "s j" 'helm-semantic-or-imenu "imenu")

(defun hfj/helm-imenu-comments ()
  "Imenu display comments."
  (interactive)
  (let* ((imenu-create-index-function 'evilnc-imenu-create-index-function))
    (helm-imenu)))
(hfj/spc-bind "s c" 'hfj/helm-imenu-comments "imenu comments")

(defun hfj/swoop-search ()
  (interactive)
  (er/mark-symbol)
  (let* ((forward t)
         (regex-p nil)
         (str (buffer-substring (region-beginning) (region-end))))
    (evil-push-search-history str forward)
    (setq evil-regexp-search regex-p)
    (setq isearch-string str)
    (isearch-update-ring str regex-p))
  (helm-swoop))
(hfj/spc-bind "s s" 'hfj/swoop-search "swoop")

(hfj/spc-bind "s f" 'helm-do-ag "search from dir")
(hfj/spc-bind "s l" 'helm-resume "resume")

search/highlight: SPC s h

(hfj/spc-declare-subtree "s h" "highlight")
(hfj/spc-bind "s h h"
              #'(lambda ()
                  (interactive)
                  (if global-highlight-thing-mode
                      (progn
                        (global-highlight-thing-mode 0)
                        ;; Set prefix to force face selection
                        (let ((current-prefix-arg t))
                          (highlight-symbol-at-point))
                        (global-highlight-thing-mode 1))
                    ;; Set prefix to force face selection
                    (let ((current-prefix-arg t))
                      (highlight-symbol-at-point))))
              "highlight symbol")
(hfj/spc-bind "s h l" 'highlight-lines-matching-regexp "lines by regex")
(hfj/spc-bind "s h p" 'highlight-phrase "phrase")
(hfj/spc-bind "s h r" 'highlight-regexp "regex")
(hfj/spc-bind "s h u" 'unhighlight-regexp "unhighlight regex")
(hfj/spc-bind "s h w" 'hi-lock-write-interactive-patterns "write interactive patterns")
(hfj/spc-bind "s h f" 'hi-lock-find-patterns "find patterns")

select: SPC v

(hfj/spc-bind "v" 'er/expand-region "expand select")

smartparens: SPC k

(hfj/spc-bind "k" 'hydra-smartparens/body "smartparens")

text: SPC x

(hfj/spc-declare-subtree "x" "text")

(hfj/spc-declare-subtree "x r" "replace")
(hfj/spc-bind "x r w" 'hfj/replace-word-at-point "replace word")
(hfj/spc-bind "x r s" 'hfj/replace-symbol-at-point "replace symbol")

(hfj/spc-bind "x w" 'whitespace-cleanup "clean whitespace")

toggles: SPC t

(hfj/spc-bind "t" 'hydra-toggles/body "toggles")

universal argument: SPC u

(defun hfj/inc-uni-arg (arg)
  (interactive "P")
  (cond ((null arg)
         (universal-argument))
        (t
         (universal-argument-more arg))))
(hfj/spc-bind "u" 'hfj/inc-uni-arg "uni-arg")

windows: SPC w

(hfj/spc-declare-subtree "w" "windows")
(hfj/spc-bind "w h" 'evil-window-left "jump left")
(hfj/spc-bind "w l" 'evil-window-right "jump right")
(hfj/spc-bind "w j" 'evil-window-down "jump down")
(hfj/spc-bind "w k" 'evil-window-up "jump up")

(hfj/spc-bind "w H" 'buf-move-left "swap left")
(hfj/spc-bind "w L" 'buf-move-right "swap right")
(hfj/spc-bind "w J" 'buf-move-down "swap down")
(hfj/spc-bind "w K" 'buf-move-up "swap up")

(hfj/spc-bind "w s" 'evil-window-split "h-split")
(hfj/spc-bind "w v" 'evil-window-vsplit "v- split")

(hfj/spc-bind "w d" 'delete-window "close")
(hfj/spc-bind "w u" 'winner-undo "undo")

(hfj/spc-bind "w m" 'delete-other-windows "maximize")

(hfj/spc-bind "w w" 'ace-window "jump")
(hfj/spc-bind "w W" 'ace-swap-window "swap")

(hfj/spc-bind "w t" 'hfj/toggle-window-dedicated "dedicate")
(hfj/spc-bind "w f" 'fit-window-to-buffer "fit window")

(hfj/spc-bind "w i" 'hfj/clone-indirect-buffer-here "clone indirect")

Post hooks

(run-hooks 'hfj/post-config-hook)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment