1
0
mirror of https://github.com/Foltik/dotfiles synced 2024-11-30 14:32:08 -05:00
dotfiles/lain/.emacs.d/config.org

56 KiB
Raw Blame History

Packaging

Package.el

Package.el is the built-in package manager in Emacs.

Set up all the repositories that we're going to be downloading stuff from.

This is where the fun begins.

  (require 'package)
  (setq package-archives
        '(("gnu"          . "https://elpa.gnu.org/packages/")
          ("melpa"        . "https://melpa.org/packages/")
          ("melpa-stable" . "https://stable.melpa.org/packages/")
          ("marmalade"    . "https://marmalade-repo.org/packages/")
          ("org"          . "https://orgmode.org/elpa/")))
  (setq package-enable-at-startup nil)
  (package-initialize)

use-package

use-package is a nifty macro that interfaces with Package.el, keeping package-specific configuration all in once place. It's pretty much the basis of this entire config.

  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package))

  (eval-when-compile
    (require 'use-package))

  (setq use-package-compute-statistics t)
  (setq use-package-always-ensure t)
  (setq use-package-always-pin "melpa")
  (setq use-package-verbose t)

Custom Keywords

Useful :keyword macros that extend the vanilla use-package functionality.

:company

Automatically appends a company backend to the company backends alist when a mode is entered.

  (require 'derived)

  (defun use-package-company-normalize (name keyword args)
    "Normalize the KEYWORD with NAME :company with arguments ARGS into a list of pairs for the handler."
    (use-package-as-one (symbol-name keyword) args
      (lambda (label arg)
        (unless (or (consp arg) (use-package-non-nil-symbolp arg))
          (use-package-error
            (concat
              label
              "<symbol> or "
              "(<symbol or list of symbols> . <symbol or function>) or "
              "a list of these")))
        (use-package-normalize-pairs
          (lambda (k)
            (or (use-package-non-nil-symbolp k)
                (and (consp k)
                     (not (cdr (last k)))
                     (seq-every-p 'use-package-non-nil-symbolp k))))
          #'use-package-recognize-function
          name label arg))))

  ;;;###autoload
  (defun use-package-company-handler (name _keyword args rest state)
    "Generate a function and hook from each pair in NAME ARGS for the keyword with NAME :company, appending the forms to the use-package declaration specified by REST and STATE."
    (use-package-concat
     (use-package-process-keywords name rest state)
     (mapcan
      (lambda (def)
        (let ((modes (car def))
              (backend (cdr def))
              (fun (intern (concat "use-package-company-add-" (symbol-name (cdr def))))))
          (when backend
            (append
             `((defun ,fun ()
                 (setq-local company-backends
                             (append company-backends '(,backend)))))
             (mapcar
              (lambda (mode)
                `(add-hook
                  ',(derived-mode-hook-name mode)
                  #',fun))
              (if (use-package-non-nil-symbolp modes) (list modes) modes))))))
      (use-package-normalize-commands args))))

  (defalias 'use-package-normalize/:company 'use-package-company-normalize)
  (defalias 'use-package-handler/:company 'use-package-company-handler)
  (defalias 'use-package-autoloads/:company 'use-package-autoloads-mode)

  (setq use-package-keywords
    (let ((idx (+ 1 (cl-position :hook use-package-keywords))))
      (append
        (seq-subseq use-package-keywords 0 idx)
        (list :company)
        (nthcdr idx use-package-keywords))))

Keybind Helpers

which-key

which-key displays a popup in the minibuffer that shows and describes keybindings prefixed by incomplete commands.

  (use-package which-key
    :diminish
    :config
    (which-key-mode))

General

General is an excellent keybind manager that adds tons of useful macros.

We first use it to set up a leader key, SPC, and prefixes; like \ in Vim.

  (use-package general
    :config
    (general-create-definer jf-leader-def
      :keymaps 'override
      :states '(normal motion insert emacs)
      :prefix "SPC"
      :non-normal-prefix "M-SPC")

    (defun jf-create-wk-prefix (desc)
      "Helper for creating which-key prefix descriptions.
  Bind to a key with general to make which-key show DESC
  as the prefix's description"
      `(:ignore t :wk ,desc))

    (defmacro jf-create-definers (definitions)
      "A wrapper for general-create-definer.
    For every pair in DEFINITIONS, creates a leader
    with name jf-NAME-def and keybind SPC KEY or M-SPC KEY in normal mode."
      `(progn
         ,@(mapcan
            (lambda (def)
              (let ((key  (car def))
                    (name (cdr def)))
                `((general-create-definer ,(intern (concat "jf-" name "-def"))
                    :keymaps 'override
                    :states '(normal insert emacs)
                    :prefix ,(concat "SPC " key)
                    :non-normal-prefix ,(concat "M-SPC " key))
                  (jf-leader-def ,key ',(jf-create-wk-prefix name)))))
            definitions)))

    (jf-create-definers
     (("a" . "apps")
      ("b" . "buffers")
      ("f" . "files")
      ("g" . "git")
      ("h" . "help")
      ("m" . "major")
      ("o" . "org")
      ("p" . "projects")
      ("w" . "windows"))))

Macros

jf-kbd

Helper functions for defining procedural keyboard macros

  (defun jf-replace-regexps-in-string (str regexps)
    "Replace all pairs of (regex . replacement) defined by REGEXPS in STR."
    (if (null regexps)
        str
      (jf-replace-regexps-in-string
       (replace-regexp-in-string (caar regexps) (cdar regexps) str t)
       (cdr regexps))))

  (defun jf-kbd (str)
    "Convert STR into a keyboard macro string by replacing terminal key sequences with GUI keycodes."
    (let ((jf-kbd-regex '(("ESC" . "<escape>")
                          ("DEL" . "<delete>")
                          ("BS"  . "<backspace>")
                          ("RET" . "<return>")
                          ("SPC" . "<SPC>")
                          ("TAB" . "<tab>"))))
      (jf-replace-regexps-in-string str jf-kbd-regex)))

  (defun jf-kbd-exec (str)
    "Execute the key sequence defined by STR. Terminal based keys are expanded to their graphical counterparts."
    (let ((minibuffer-message-timeout 0))
      (execute-kbd-macro (read-kbd-macro (jf-kbd str)))))

  (defmacro jf-kbd-defmacro (name &rest forms)
    "Create an interactive function NAME with body FORMS, where each form is executed as a keyboard macro."
    `(defun ,name ()
       (interactive)
       ,@(mapcan (lambda (form) `((jf-kbd-exec ,form))) forms)))

Vim Emulation

Evil

Evil is pretty much the entirety of Vim in Emacs.

Without it I would probably still be using vim.

  (use-package evil
    :diminish undo-tree-mode

    :preface
    (defun jf-window-split ()
      (interactive)
      (evil-window-split)
      (evil-window-down 1))
    (defun jf-window-vsplit ()
      (interactive)
      (evil-window-vsplit)
      (evil-window-right 1))

    :config
    (evil-mode t)

    (jf-windows-def
     "d" #'delete-window
     "-" #'jf-window-split
     "=" #'jf-window-vsplit
     "b" #'balance-windows
     "J" #'evil-window-bottom
     "K" #'evil-window-top
     "h" #'evil-window-left
     "j" #'evil-window-down
     "k" #'evil-window-up
     "l" #'evil-window-right
     "o" #'other-frame)

    :general
    (:keymaps 'override
     :states 'normal
     "C-x C-e" #'eval-region)

    :custom
    (evil-want-integration t)
    (evil-want-keybinding nil)
    (evil-want-fine-undo t))

Evil Collection

Evil Collection adds Evil bindings for all the parts of Emacs that Evil doesn't cover properly by default.

  (use-package evil-collection
    :after evil
    :config
    (evil-collection-init
     '(calendar
       cmake-mode
       company
       compile
       custom
       debug
       dired
       doc-view
       elisp-mode
       elisp-refs
       eshell
       eval-sexp-fu
       flycheck
       flymake
       grep
       help
       ibuffer
       image
       image-dired
       info
       ivy
       rjsx-mode
       log-view
       man
       neotree
       python
       racer
       realgud
       which-key)))

Evil Extensions

Avy

An enhanced version of f in Vim.

  (use-package avy
    :general
    (:keymaps 'override
      :states 'normal
      "C-f" 'avy-goto-char-in-line
      "C-F" 'avy-goto-char))

Subword

Make boundaries between words in camelCase act as separate words for evil motions.

This is a departure from default vim behavior and down to personal preference; disable it if you don't like it, however I find it is useful.

  (use-package subword
    :after evil
    :init
    (define-category ?U "Uppercase")
    (define-category ?u "Lowercase")
    (modify-category-entry (cons ?A ?Z) ?U)
    (modify-category-entry (cons ?a ?z) ?u)

    :config
    (push '(?u . ?U) evil-cjk-word-separating-categories))

evil-surround

Bind S and a delimiter to surround in visual mode.

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

evil-goggles

Flash highlight on evil motions for better visual feedback of what's happening.

(use-package evil-goggles
    :config
    (evil-goggles-mode)
    (evil-goggles-use-diff-faces))

Emacs

Defaults

Add to Load Path

Create and add a folder to the load path for local lisp files.

The folder itself and all descendants will be added to the path.

These packages will take precedence over other libraries with the same name.

  (unless (file-exists-p jf-load-path)
    (make-directory jf-load-path))

  (let ((default-directory jf-load-path))
    (setq load-path
          (append
           (let ((load-path (copy-sequence load-path)))
             (append
              (copy-sequence (normal-top-level-add-to-load-path '(".")))
              (normal-top-level-add-subdirs-to-load-path)))
           load-path)))

File Not Found Functions

Offer to create nonexistant parent folders when a new file is opened

  (defun jf-create-nonexistant-directories ()
    (let ((parent-directory (file-name-directory buffer-file-name)))
      (when (and (not (file-exists-p parent-directory))
                (y-or-n-p (format "Directory `%s' does not exist. Create it?" parent-directory)))
        (make-directory parent-directory t)))) ; last argument specifies to behave like `mkdir -p'

  (add-to-list 'find-file-not-found-functions #'jf-create-nonexistant-directories)

Customize Location

Make changes in M-x customize go somewhere other than being schlunked into init.el.

  (setq custom-file (concat user-emacs-directory "customize.el"))
  (load custom-file t)

Disable Bell

Shut up, emacs.

  (setq ring-bell-function #'ignore)

Shorter Prompts

Make yes-or-no prompts ask for y-or-n instead. Saves loads of time™.

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

Move Backup Files

By default, emacs gunks up every folder with file~ backups and #file# lockfiles. Schlunk them all in .emacs.d/saves instead.

  (let ((save-dir (locate-user-emacs-file "saves")))
    (setq backup-directory-alist
          `((".*" . ,save-dir)))
    (setq auto-save-file-name-transforms
          `((".*" ,(concat save-dir "/") t))))

Disable lockfiles

On a single user system, lockfiles only serve to prevent concurrent editing from the same instance.

They are sometimes annoying, so just disable them.

  (setq create-lockfiles nil)

Secure auth-source

GPG encrypt stored auth tokens from auth-source instead of storing them in plaintext.

  (setq auth-sources '("~/.emacs.d/authinfo.gpg"))

Use UTF-8

Pleeeease default to UTF-8, Emacs.

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

Trash when Deleting

Don't permanently delete stuff unless asked.

  (setq delete-by-moving-to-trash t)

Open Compressed Files

…automatically.

  (setq auto-compression-mode t)

Save Minibuffer History

  (savehist-mode 1)
  (setq history-length 1000)

Double Spaces

Why sentences would need double spaces to end I do not know.

  (set-default 'sentence-end-double-space nil)

Eval Print Level

Print more stuff when running C-x C-e or (eval-last-sexp)

  (setq eval-expression-print-level 100)

Inhibit GC in Minibuffer

Don't garbage collect while the minibuffer is open, as heavy things like completion and searches are happening and will slow down with many garbage collections.

  (add-hook 'minibuffer-setup-hook #'jf-inhibit-gc)
  (add-hook 'minibuffer-exit-hook  #'jf-resume-gc)

Default Shell

I use fish and it breaks some things when used as the default shell, so just explicitly tell emacs to use bash.

(setq-default shell-file-name "/bin/bash")

Scratch buffer message

(setq initial-scratch-message ";; This buffer is for temporary text that is not saved, and for Lisp evaluation.
;; To create a file, visit it with \\[counsel-find-file] and enter text in its buffer.")

UI

Font

Set up a nice coding font with ligatures.

  (add-to-list 'default-frame-alist '(font . "Fira Code 12"))
  (set-face-attribute 'default t :font "Fira Code 12")

Menu Bar

Disable the useless cruft at the top of the screen.

  (menu-bar-mode -1)
  (tool-bar-mode -1)
  (scroll-bar-mode -1)

Modeline

Diminish

Adds support for :diminish in use-package declarations, which hides a mode from the modeline.

  (use-package diminish)
Column Number

Show line and column numbers in the modeline.

  (setq line-number-mode t)
  (setq column-number-mode t)

Line Numbers

Use the default emacs relative line numbers, but switch to absolute lines when in insert mode.

  (add-hook 'prog-mode-hook (lambda () (setq display-line-numbers 'absolute)))
  (add-hook 'evil-insert-state-entry-hook (lambda () (when (bound-and-true-p display-line-numbers) (setq display-line-numbers 'absolute))))
  (add-hook 'evil-insert-state-exit-hook (lambda () (when (bound-and-true-p display-line-numbers) (setq display-line-numbers 'relative))))
  (add-hook 'evil-normal-state-entry-hook (lambda () (when (bound-and-true-p display-line-numbers) (setq display-line-numbers 'relative))))
  (add-hook 'evil-normal-state-entry-hook (lambda () (when (bound-and-true-p display-line-numbers) (setq display-line-numbers 'absolute))))
  (add-hook 'evil-visual-state-entry-hook (lambda () (when (bound-and-true-p display-line-numbers) (setq display-line-numbers 'relative))))
  (add-hook 'evil-visual-state-entry-hook (lambda () (when (bound-and-true-p display-line-numbers) (setq display-line-numbers 'absolute))))
  (add-hook 'display-line-numbers-hook (lambda () (when (evil-normal-state-p) (setq display-line-numbers 'relative))))

Show Matching Parens

Highlights matching parenthesis when the point is near one

  (require 'paren)
  (setq show-paren-delay 0)
  (show-paren-mode)

Scrolling

Scroll smooth-ish-ly instead of jarring jumps.

  ;;(use-package smooth-scroll
  ;;  :config
  ;;  (smooth-scroll-mode t))

Dashboard

Show a cool custom dashboard buffer on startup.

  (use-package dashboard
    :diminish page-break-lines-mode
    :config
    (dashboard-setup-startup-hook)
    (setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))

    :custom
    (dashboard-startup-banner 'logo)
    (dashboard-banner-logo-title "Welcome to Electronic Macs.")
    (dashboard-items
     '((recents . 5)
       (agenda)
       (bookmarks . 5)
       (registers . 5))))

Themes

pywal

Fancy dynamic color scheme generation from desktop wallpapers.

Requires additional setup per-machine.

  (defvar jf-theme-pywal-path "~/.cache/wal/colors.el" "Path to the colorscheme generated by pywal.")

  (defun jf-theme-pywal ()
    (load-file jf-theme-pywal-path))

spacemacs

A pretty fancy theme that has lots of supported modes.

  (unless (package-installed-p 'spacemacs-theme)
    (package-install 'spacemacs-theme))

  (defun jf-theme-spacemacs ()
    (load-theme 'spacemacs-dark))

Transparency

Sets the window's transparency, to better admire choice wallpapers.

The first number in the alpha section applies when the window is active, the second when it's inactive.

  (set-frame-parameter (selected-frame) 'alpha 85)
  (add-to-list 'default-frame-alist '(alpha . 85))

Helpers

Keep track of the current theme and apply it at startup

  (defvar jf-theme #'jf-theme-spacemacs "Theme function to call.")

  (defun jf-apply-theme ()
    "Apply the current theme as set by jf-theme."
    (funcall jf-theme))

  (jf-apply-theme)

Keybinds

Files

Define and bind functions for managing files, including emacs config files.

  (defun jf-edit-config ()
    (interactive)
    (find-file jf-config-org-file))

  (defun jf-edit-init ()
    (interactive)
    (find-file jf-init-file))

  (defun jf-reload-config ()
    (interactive)
    (org-babel-load-file jf-config-file))

  (defun jf-byte-compile-config ()
    (interactive)
    (byte-compile-file jf-init-file)
    (byte-compile-file jf-config-file))

  (jf-files-def
    "r"  #'revert-buffer
    "e"  (jf-create-wk-prefix "emacs files")
    "ec" #'jf-edit-config
    "ei" #'jf-edit-init
    "er" #'jf-reload-config
    "eb" #'jf-byte-compile-config)

Buffers

Define and bind some functions for managing buffers.

  (defun jf-kill-current-buffer ()
    (interactive)
    (kill-buffer (current-buffer)))
  (defun jf-kill-all-buffers ()
    (interactive)
    (seq-do 'kill-buffer (buffer-list)))

  (jf-buffers-def
    "B" 'ibuffer
    "c" 'jf-kill-current-buffer
    "C" 'jf-kill-all-buffers)

Organization

Capture Templates

All capture templates, from tasks to bookmarks. Capture dispatch is invoked with `SPC o c`.

Tasks

  (setq jf-org-capture-task-templates
        '(("t" "Todo")
          ("tg" "General" entry
           (file "refile.org")
           "**** TODO %^{task}\n%U\n%a\n%?\n")
          ("tt" "General (Date)" entry
           (file "refile.org")
           "**** TODO %^{task}\n%U\nDue: %^t\n%a\n%?\n")
          ("tT" "General (Date+Time)" entry
           (file "refile.org")
           "**** TODO %^{task}\n%U\nDue: %^T\n%a\n%?\n")
          ("tst" "School" entry
           (file "refile.org")
           "**** TODO %^{task}\n%U\n%a\nDue: %^t\nClass: %^{class}\n%?\n")
          ("tss" "School (Date)" entry
           (file "refile.org")
           "**** TODO %^{todo}\n%U\n%a\nDue: %^t\nClass: %^{class}\n%?\n")
          ("tsS" "School (Date+Time)" entry
           (file "refile.org")
           "**** TODO %^{todo}\n%U\n%a\nDue: %^T\nClass: %^{class}\n%?\n")
          ("n" "Note" entry
           (file "refile.org")
           "** %? :NOTE:\n%U\n%a\n")))

Bookmarks

  (setq jf-org-capture-bookmark-templates
        '(("b" "Bookmark" entry
           (file "refile.org")
           "** [[%^{link}][%^{name}]] :LINK:\n%U\n%?")))

Personal

  (setq jf-org-capture-personal-templates
        '(("j" "Journal")
          ("jj" "Journal Entry" entry
           (file+olp+datetree "journal.org")
           "**** Today's Events\n%?")
          ("jt" "Thoughts" entry
           (file+olp+datetree "journal.org")
           "**** Thought\n%^g%^{summary}\n%U\n%?")
          ("jd" "Dream Journal Entry" entry
           (file+olp+datetree "journal.org")
           "**** Today's Dreams %^g\n%?")))

Protocol

  (setq jf-org-capture-protocol-templates
        '(("w" "Website [Protocol]" entry
           (file "refile.org")
           "** [[%:link][%:description%?]] :LINK:\n%U\n%a\n%i\n%?")))

All

Tie it all together.

  (setq jf-org-capture-templates
        (append
         jf-org-capture-task-templates
         jf-org-capture-personal-templates
         jf-org-capture-bookmark-templates
         jf-org-capture-protocol-templates))

Hooks

Temporarily widens the capture mode buffer and loads the tag completion alist with the full buffer tags.

  (defun jf-update-capture-tags ()
    (save-restriction
      (widen)
      (setq-local org-tag-alist (org-get-buffer-tags))))

Structure Templates

Defines expansions with < followed by a string in org-mode.

For example, <el TAB will insert an elisp source block.

Source Blocks

  (setq jf-org-source-structure-templates
        '(("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC")
          ("ipy" "#+BEGIN_SRC ipython :session :results raw drawer\n?\n#+END_SRC")))

Structure

  (setq jf-org-structure-templates '())

All

Tie it all together.

  (setq jf-org-structure-templates
        (append
         jf-org-structure-templates
         jf-org-source-structure-templates))

Structure Editing

Refile/Copy Targets

Goodize the refiling targets to allow refiling and copying to arbitrary subtrees.

Useful within org-capture to refile to a specific deeper heading in a file.

  (defun jf-org-deep-refile ()
    (interactive)
    (setq-local org-refile-targets '((nil :maxlevel . 5)
                                     (org-agenda-files :maxlevel . 3)))
    (setq-local org-refile-use-outline-path t)
    (org-refile))
  (defun jf-org-deep-copy ()
    (interactive)
    (setq-local org-refile-targets '((nil :maxlevel . 5)
                                     (org-agenda-files :maxlevel . 3)))
    (setq-local org-refile-use-outline-path t)
    (org-copy))

TODOs

States

Add some additional TODO states (C-c C-t on a heading).

The general workflow is TODO -> NEXT -> DONE, with some exceptions like WAITING and CANCELLED.

  (setq jf-org-todo-keywords
        '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
          (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "MEETING")))

  (setq jf-org-todo-faces
        '(("TODO" :foreground "red" :weight bold)
          ("NEXT" :foreground "blue" :weight bold)
          ("DONE" :foreground "forest green" :weight bold)
          ("WAITING" :foreground "orange" :weight bold)
          ("HOLD" :foreground "magenta" :weight bold)
          ("CANCELLED" :foreground "forest green" :weight bold)
          ("MEETING" :foreground "forest green" :weight bold)
          ("PHONE" :foreground "forest green" :weight bold)))

Tags and Triggers

  • Moving a task to CANCELLED adds a CANCELLED tag
  • Moving a task to WAITING adds a WAITING tag
  • Moving a task to HOLD adds WAITING and HOLD tags
  • Moving a task to a done state removes WAITING and HOLD tags
  • Moving a task to TODO removes WAITING, CANCELLED, and HOLD tags
  • Moving a task to NEXT removes WAITING, CANCELLED, and HOLD tags
  • Moving a task to DONE removes WAITING, CANCELLED, and HOLD tags
  (setq jf-org-todo-state-tags-triggers
        '((("CANCELLED" ("CANCELLED" . t))
           ("WAITING" ("WAITING" . t))
           ("HOLD" ("WAITING") ("HOLD" . t))
           (done ("WAITING") ("HOLD"))
           ("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
           ("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
           ("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))

Evil org

Spice up org mode with some deliciously evil bindings.

  (use-package evil-org
    :after (evil org)
    :hook (org-mode . evil-org-mode)
    :config
    (evil-org-set-key-theme '(navigation insert textobjects additional calendar))
    (require 'evil-org-agenda)
    (evil-org-agenda-set-keys))

Pretty bullets

Make bullets indent content and look choice

  (use-package org-bullets
    :after org
    :hook (org-mode . org-bullets-mode))

Org

Keep org-mode up to date straight from the cow's utters. If the manual is not on your computer, it's here.

  (use-package org
    :pin org
    :mode ("\\.org\\'" . org-mode)
    :hook ((org-mode . org-indent-mode)
           (org-capture-mode . evil-insert-state)
           (org-capture-mode . jf-update-capture-tags)
           (org-babel-after-execute . org-display-inline-images))

    :config
    ;; Needed to save clock history across emacs
    (org-clock-persistence-insinuate)

    :general
    (jf-major-def
      :keymaps 'org-mode-map
      "e" 'org-export-dispatch
      "a" 'org-attach)
    (jf-org-def
      "a" 'org-agenda
      "A" 'org-archive-to-archive-sibling
      "c" 'org-capture
      "C i" 'org-clock-in
      "C o" 'org-clock-out
      "C c" 'org-clock-cancel
      "l" 'org-insert-link
      "L" 'org-store-link
      "b" 'org-switchb
      "r" 'jf-org-deep-refile
      "R" 'jf-org-deep-copy)

    :custom
    ;; Base options
    (org-directory "~/Sync/org" "Default directory for all things org")
    (org-default-notes-file "notes.org")
    ;; Agenda
    (org-agenda-files '("~/Sync/org/"))
    (org-agenda-include-diary t)
    (org-clock-persist 'history "Save clock history across emacs")
    ;; TODO and capture stuff
    (org-todo-keywords jf-org-todo-keywords)
    (org-todo-keyword-faces jf-org-todo-faces)
    (org-todo-state-tags-triggers jf-org-todo-state-tags-triggers)
    (org-use-fast-todo-selection t "Use a prefix key instead of manual cycling for TODOs")
    (org-capture-templates jf-org-capture-templates)
    ;; Code Stuff
    (org-structure-template-alist (append org-structure-template-alist jf-org-structure-templates))
    (org-babel-load-languages '((emacs-lisp . t) (ipython . t)))
    (org-src-window-setup 'current-window "Edit source code in the current window")
    (org-src-fontify-natively t "Highlight syntax in source blocks")
    (org-latex-listings 'minted "Export org source blocks as code envs, not {verbose}")
    (org-latex-packages-alist '(("" "minted")) "Add the minted package for code blocks to all org files")
    (org-latex-to-pdf-process '("latexmk -e '$latex=q/latex %O -shell-escape %S/' -f pdf %f") "Use a single call to latexmk for export")
    (org-treat-S-cursor-todo-selection-as-state-change nil "Disable S-cursor on TODOs to prevent avoiding the transition hooks")
    (org-confirm-babel-evaluate nil "Don't ask every single time if I *really* want to evaluate that source block")
    (org-startup-with-inline-images t "Load inline images when first opening a buffer"))

Communication

Web

Tools

Fuzzy Matching

Most facilities are provided by Ivy and friends, which build on existing emacs commands.

Smex

While the actual smex command is not in use, counsel-M-x will use it for sorting by usage.

(use-package smex)

Ivy

Ivy has most of the buffer-related commands.

  (use-package ivy
    :init
    (defun jf-kill-current-buffer ()
      (interactive)
      (kill-buffer (current-buffer)))
    (defun jf-kill-all-buffers ()
      (interactive)
      (seq-do 'kill-buffer (buffer-list)))

    :general
    (jf-buffers-def
      "b" 'ivy-switch-buffer
      "v" 'ivy-push-view
      "V" 'ivy-pop-view)

    :custom
    (ivy-use-virtual-buffers t)
    (ivy-count-format "%d/%d"))

Counsel

A collection of ivy enhanced versions of common Emacs commands.

Bind up some useful ones.

  (use-package counsel
    :general
    ("M-x" 'counsel-M-x)
    (jf-leader-def
      :states 'normal
      "x" 'counsel-M-x)
    (jf-files-def
      "f" 'counsel-find-file)
    (jf-help-def
      "a" 'counsel-apropos
      "f" 'counsel-describe-function
      "k" 'describe-key
      "K" 'counsel-descbinds
      "l" 'counsel-find-library
      "s" 'counsel-info-lookup-symbol
      "u" 'counsel-unicode-char
      "v" 'counsel-describe-variable))

Swiper

An ivy-ified replacement for isearch.

  (use-package swiper
    :after evil
    :general
    (:keymaps 'override
      :states 'normal
      "/" 'swiper
      "n" 'evil-search-previous
      "N" 'evil-search-next))

Neotree

A cool toggleable directory structure sidebar. It needs icon fonts, installed with M-x all-the-icons-install-fonts.

  (use-package all-the-icons)
  (use-package neotree
    :after all-the-icons
    :general
    (jf-apps-def
      "t" 'neotree-toggle)
    :custom
    (neo-theme (if (display-graphic-p) 'icons 'arrow)))

Ranger

Brings the glory of Ranger to Emacs.

  (use-package ranger
    :commands (ranger deer))

Sunshine

Allows retrieving OpenWeatherMap forecasts in the minibuffer.

  (use-package sunshine
    :commands sunshine-forecast
    :general
    (jf-apps-def
     "w" #'sunshine-forecast)
    :custom
    (sunshine-location "Piscataway, US")
    (sunshine-units 'metric)
    (sunshine-appid "7caf100277f14845e7f354c6590a09cb")
    (sunshine-show-icons t))

Kubernetes

A porcelain like magit for kubernetes.

  ;;(use-package kubernetes
  ;;    :commands kubernetes-overview)

  ;;(use-package kubernetes-evil
  ;;    :after kubernetes)

Programming

Formatting

Word Wrap

Add a command for setting the word wrap column. When enabled, use M-q to wrap the current block.

  (defun jf-word-wrap (column)
    "Enable auto refill mode at COLUMN."
    (interactive "nFill column: ")
    (setq-local fill-column column)
    (refill-mode))

  (defun jf-word-wrap-off ()
    "Disable auto refill mode."
    (interactive)
    (refill-mode))

Indentation

How code should be 'dented.

Defaults

Set some sane defaults for indentation.

(setq jf-tab-width 4)
(setq-default python-indent-offset jf-tab-width)
(setq-default evil-shift-width jf-tab-width)
(setq-default c-basic-offset jf-tab-width)
; Disable annoying electric indent of previous lines
(setq-default electric-indent-inhibit t)
; Eat the whole tab when I press backspace
(setq backward-delete-char-untabify-method 'hungry)
Helpers

Define some useful helper functions related to indentation used later in the config.

  (defun jf-indent-tabs (width)
    (interactive "nTab width: ")
    (setq tab-width width)
    (local-set-key (kbd "TAB") 'tab-to-tab-stop)
    (setq indent-tabs-mode t))

  (defun jf-indent-spaces (num)
    (interactive "nNumber of spaces: ")
    (setq js2-basic-offset num)
    (setq tab-width num)
    (setq indent-tabs-mode nil))

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

  ;; Default to 4 spaces
  (add-hook 'prog-mode-hook #'jf-indent-4-spaces)

  ;; Define functions for every level of indent that might need hooking
  (cl-macrolet
      ((jf-define-indent-funs (widths)
         `(progn
            ,@(mapcan
               (lambda (num)
                 `((defun ,(intern (concat "jf-indent-" (number-to-string num) "-spaces")) ()
                     (jf-indent-spaces ,num))
                   (defun ,(intern (concat "jf-indent-tabs-" (number-to-string num))) ()
                     (jf-indent-tabs ,num))))
               widths))))
    (jf-define-indent-funs (2 4 8)))

  ;; TODO: Replace with dedicated whitespace config
  (setq whitespace-style '(face tabs tab-mark trailing))
  (custom-set-faces
     '(whitespace-tab ((t (:foreground "#636363")))))

  ;; Make tabs visible
  (setq whitespace-display-mappings
     '((tab-mark 9 [124 9] [92 9])))

  (add-hook 'prog-mode-hook #'whitespace-mode)

Parentheses

Smartparens

Smartparens handles electric parens and navigation for languages that aren't lispy.

  (use-package smartparens
    :diminish
    :commands smartparens-mode
    :config
    (require 'smartparens-config))
ParEdit

And ParEdit handles the rest.

  (use-package paredit
    :diminish
    :commands enable-paredit-mode)
Lispyville

And now Lispyville handles the rest.

  (use-package lispyville
    :commands lispyville-mode
    :config
    (lispyville-set-key-theme
     '(operators
       c-w
       slurp/barf-cp
       commentary
       (escape insert)
       (additional-movement normal visual motion))))
Evil-Cleverparens

Evil-Cleverparens adds additional features to Evil all about working with sexps, including keeping parens balanced when using commands like dd.

  (use-package evil-cleverparens
    :diminish
    :commands evil-cleverparens-mode)
Activation

Add a hook to enable paren helper modes for any prog-mode buffer

  (defun jf-paren-mode ()
    "Pick a suitable parenthesis mode for the current major mode."
    (electric-pair-mode)
    (if (member major-mode '(emacs-lisp-mode
                             lisp-mode
                             lisp-interaction-mode
                             scheme-mode))
        (lispyville-mode)
      (smartparens-mode)))

  (add-hook 'prog-mode-hook #'jf-paren-mode)
Helpers

Helpers for wrangling sexps

  (jf-kbd-defmacro jf-wrap-fn-inline
    "ESC i C-q { RET TAB ESC jI} SPC ESC k^")

  (jf-kbd-defmacro jf-wrap-fn-line
    "ESC kA SPC C-q { ESC jjI} SPC ESC k^")

  (jf-kbd-defmacro jf-wrap-fn-sexp
    "ESC i C-q { RET TAB ESC )i} ESC i RET ESC k^")

Whitespace

ws-butler

Unobtrusively cleans up whitespace before EOLs as you edit, stopping the noisy commits generated from blanket trimming entire files.

  (use-package ws-butler
    :hook (prog-mode . ws-butler-mode))

pretty-mode

pretty-mode redisplays parts of the Emacs buffer as pretty symbols.

  (use-package pretty-mode
    :hook (prog-mode . pretty-mode)
    :config
    (pretty-deactivate-groups '(:equality
                                :ordering
                                :ordering-double
                                :ordering-triple
                                :arrows
                                :arrows-twoheaded
                                :punctuation
                                :logic
                                :sets))

    (pretty-activate-groups '(:sub-and-superscripts
                              :greek
                              :arithmetic-nary)))

Prettify-Symbols-Mode

Allows custom unicode replacement of symbols. Fill in the gaps where pretty-mode left off. See list of unicode symbols that can be used here.

Python
  (defun jf-prettify-python ()
    (dolist (pair
           ;; Syntax
          '(("def" .      #x2131)
           ("not" .      #x2757)
           ("in" .       #x2208)
           ("not in" .   #x2209)
           ("return" .   #x27fc)
           ("yield" .    #x27fb)
           ("for" .      #x2200)
           ;; Base Types
           ("int" .      #x2124)
           ("float" .    #x211d)
           ("str" .      #x1d54a)
           ("True" .     #x1d54b)
           ("False" .    #x1d53d)
           ;; Mypy
           ("Dict" .     #x1d507)
           ("List" .     #x2112)
           ("Tuple" .    #x2a02)
           ("Set" .      #x2126)
           ("Iterable" . #x1d50a)
           ("Any" .      #x2754)
           ("Union" .    #x22c3)))
      (push pair prettify-symbols-alist)))

  (add-hook 'python-mode-hook #'prettify-symbols-mode)
  (add-hook 'python-mode-hook #'jf-prettify-python)

Pretty fonts

Enables ligatures from fira-code to be used with prettify-symbols-mode.

  (use-package pretty-fonts
    :load-path jf-load-path
    :config
    (pretty-fonts-add-hook 'prog-mode-hook pretty-fonts-fira-code-alist)
    (pretty-fonts-add-hook 'org-mode-hook pretty-fonts-fira-code-alist)
    (pretty-fonts-set-fontsets-for-fira-code))

Rainbow Delimiters

Make it easier to spot sexp depth by pairing matching delimiters with rainbow colors.

  (use-package rainbow-delimiters
    :hook (prog-mode . rainbow-delimiters-mode))

Checkers

Flycheck

Flycheck highlights syntax errors in a few languages.

  (use-package flycheck
    :hook (prog-mode . flycheck-mode)
    :custom (flycheck-disabled-checkers '(emacs-lisp-checkdoc)))

Column 80 Highlight

Add a hotkey for highlighting column 80 in prog-mode

  (use-package fill-column-indicator
    :init
    (setq fci-rule-use-dashes t)
    (setq fci-rule-column 80)
    :general
    (jf-major-def
      :keymaps 'prog-mode-map
      "8" 'fci-mode))

Completion

Company

Company auto-completes stuff in the buffer, and company-quickhelp shows documentation popups when idling on a completion candidate.

  (use-package company
    :hook (prog-mode . company-mode)
    :general
    (:keymaps 'company-active-map
              "C-SPC" 'company-abort)

    :custom
    (company-maximum-prefix-length 2)
    (company-idle-delay 0.2 "Decrease idle delay"))

  (use-package company-quickhelp
    :after company
    :hook (company-mode . company-quickhelp-mode))

Snippets

Yasnippet adds support for custom snippets, invoked by inputting a prefix and pressing TAB.

  (use-package yasnippet
    :hook (prog-mode . yas-minor-mode)
    :custom
    (yas-snippet-dirs
     '("~/.emacs.d/snippets"
       "~/.emacs.d/elpa/yasnippet-snippets-0.6/snippets")))

Debugging

Realgud

Realgud is a modular frontend for many debuggers, right in Emacs.

  (use-package realgud
    :commands
    (realgud:gdb
     realgud:lldb
     realgud:node-inspect
     realgud:pdb
     realgud:trepan3k))

RMSbolt

RMSbolt Shows disassembly in a buffer next to code, highlighting relevant regions.

  (use-package rmsbolt
    :commands rmsbolt-mode)

Git

Magit

Core

It's magic git! Keybinds here

  (use-package magit
    :general
    (jf-git-def
      "b" 'magit-blame-addition
      "B" 'magit-blame-reverse
      "s" 'magit-status)
    (:keymaps 'magit-status-mode-map
      "i" 'jf-lcsr-issue-dispatch)
    :config
    (jf-lcsr-setup))

It's evil magic git!

(use-package evil-magit
  :after (evil magit))
LCSR Issues

My workplace has a very specific git workflow that basically boils down to creating issue/$id/$description and branching off it to issue/$id/$person for each developer, then merging back to the base. What follows are some workflow automation functions and integrations for dealing with this.

Constants
  (defvar jf-lcsr-me "jwf78")
Predicates
  (defun jf-lcsr-branch-base-p (branch)
    (string-match "^\\(origin/\\)?issue/[0-9]\\{2,4\\}/[A-Za-z-]+$" branch))

  (defun jf-lcsr-branch-user-p (branch)
    (string-match "^\\(origin/\\)?issue/[0-9]\\{2,4\\}/[a-z]\\{2,3\\}[0-9]\\{1,5\\}$" branch))

  (defun jf-lcsr-branch-origin-p (branch)
    (string-match "^origin/issue/[0-9]\\{2,4\\}/[A-Za-z-]+$" branch))

  (defun jf-lcsr-branch-my-p (branch)
    (and (jf-lcsr-branch-user-p branch) (string= jf-lcsr-me (jf-lcsr-branch-tag branch))))

  (defun jf-lcsr-branch-p (branch)
    (or (jf-lcsr-branch-base-p branch) (jf-lcsr-branch-user-p branch)))

  (defun jf-lcsr-branch-id= (a b)
    (string= (jf-lcsr-branch-id a) (jf-lcsr-branch-id b)))
Splitting/Joining
  (defun jf-lcsr-branch (id tag)
    (string-join `("issue" ,id ,tag) "/"))

  (defun jf-lcsr-branch-id (branch)
    (let ((lst (split-string branch "/")))
      (if (jf-lcsr-branch-origin-p branch)
          (elt lst 2)
        (elt lst 1))))

  (defun jf-lcsr-branch-tag (branch)
    (let ((lst (split-string branch "/")))
      (if (jf-lcsr-branch-origin-p branch)
          (elt lst 3)
        (elt lst 2))))
Listing/Searching
  (defun jf-lcsr-branches ()
    (seq-filter #'jf-lcsr-branch-p (magit-list-branch-names)))

  (defun jf-lcsr-base-branches ()
    (seq-filter #'jf-lcsr-branch-base-p (jf-lcsr-branches)))

  (defun jf-lcsr-find-id (id branches)
    (seq-find
     (lambda (b) (string= (jf-lcsr-branch-id b) id))
     branches))

  (defun jf-lcsr-find-tag (tag branches)
    (seq-find
     (lambda (b) (string= (jf-lcsr-branch-tag b) tag))
     branches))
Navigation
  (defun jf-lcsr-branch-to-base (branch)
    (if (jf-lcsr-branch-user-p branch)
        (jf-lcsr-find-id (jf-lcsr-branch-id branch) (jf-lcsr-base-branches))
      branch))

  (defun jf-lcsr-branch-to-my (branch)
    (jf-lcsr-branch (jf-lcsr-branch-id branch) jf-lcsr-me))

  (defun jf-lcsr-branch-to-toggle (branch)
    (if (jf-lcsr-branch-base-p branch)
        (jf-lcsr-branch-to-my branch)
      (jf-lcsr-branch-to-base branch)))

  (defun jf-lcsr-checkout-base ()
    (interactive)
    (magit-checkout (jf-lcsr-branch-to-base (magit-get-current-branch))))

  (defun jf-lcsr-checkout-my ()
    (interactive)
    (magit-checkout (jf-lcsr-branch-to-my (magit-get-current-branch))))

  (defun jf-lcsr-checkout-toggle ()
    (interactive)
    (magit-checkout (jf-lcsr-branch-to-toggle (magit-get-current-branch))))

  (defun jf-lcsr-checkout-id (id)
    (interactive
     (list
      (completing-read
       "ID: "
       (mapcar (lambda (b) (jf-lcsr-branch-id b))
               (jf-lcsr-base-branches)))))
    (magit-checkout
     (jf-lcsr-find-id id (jf-lcsr-base-branches))))

  (defun jf-lcsr-checkout-tag (tag)
    (interactive
     (list
      (completing-read
       "Tag: "
       (mapcar (lambda (b) (jf-lcsr-branch-tag b))
               (jf-lcsr-base-branches)))))
    (magit-checkout
     (jf-lcsr-find-tag tag (jf-lcsr-base-branches))))
Commits
  (defun jf-lcsr-prepend-id (msg branch)
    (if (jf-lcsr-branch-p branch)
        (concat "#" (jf-lcsr-branch-id branch) " " msg)
      msg))

  (defun jf-lcsr-commit-message (msg)
    (jf-lcsr-prepend-id msg (magit-get-current-branch)))

  (defun jf-lcsr-merge-message (current source)
    (let ((message (concat "Merge branch " source " into " current)))
      (if (jf-lcsr-branch-id= source current)
          (jf-lcsr-prepend-id message current)
        (jf-lcsr-prepend-id (jf-lcsr-prepend-id message source) current))))

  (defun jf-lcsr-commit (msg)
    (interactive "sMessage: ")
    (let ((default-directory (magit-toplevel)))
      (magit-run-git
       "commit"
       `(,(concat "-m" (jf-lcsr-commit-message msg))))))

  (defun jf-lcsr-merge-toggle ()
    (interactive)
    (let ((current (magit-get-current-branch))
          (source (jf-lcsr-branch-to-toggle (magit-get-current-branch))))
      (magit-run-git-async
       "merge"
       `(,(concat "-m " (jf-lcsr-merge-message current source))) source)))

  (defun jf-lcsr-mergeback ()
    (interactive)
    (jf-lcsr-checkout-base)
    (jf-lcsr-merge-toggle))

  (defun jf-lcsr-merge (source)
    (interactive
     (list
      (completing-read
       "Branch: "
       (magit-list-branch-names))))
    (magit-run-git-async
     "merge"
     `(,(concat "-m " (jf-lcsr-merge-message (magit-get-current-branch) source))) source))
Issues
  (defun jf-lcsr-issue-new (id tag source)
    (interactive
     (list
      (read-string "ID: ")
      (read-string "Tag: ")
      (let ((input
             (completing-read
              "Branch Off (default master): "
              (mapcar (lambda (b) (jf-lcsr-branch-tag b))
                      (jf-lcsr-base-branches))
              nil nil nil nil "master")))
        (if (string= input "master")
            input
          (jf-lcsr-find-tag input (jf-lcsr-base-branches))))))
    (let ((base-branch (jf-lcsr-branch id tag))
          (my-branch (jf-lcsr-branch id jf-lcsr-me)))
      (magit-branch-create base-branch source)
      (magit-checkout base-branch)
      (magit-branch-create my-branch base-branch)
      (magit-checkout my-branch)))
Tracking
  (defvar jf-lcsr-dir (expand-file-name "lcsr" user-emacs-directory))
  (defvar jf-lcsr-today-file (expand-file-name "today.org" "lcsr"))
  (defvar jf-lcsr-next-file (expand-file-name "next.org" "lcsr"))
  (defvar jf-lcsr-notes-file (expand-file-name "notes.org" "lcsr"))

  (defun jf-lcsr-log-ensure ()
    (unless (file-exists-p jf-lcsr-dir)
      (make-directory jf-lcsr-dir))
    (unless (file-exists-p jf-lcsr-today-file)
      (with-temp-buffer (write-file jf-lcsr-today-file)))
    (unless (file-exists-p jf-lcsr-next-file)
      (with-temp-buffer (write-file jf-lcsr-next-file)))
    (unless (file-exists-p jf-lcsr-notes-file)
      (with-temp-buffer (write-file jf-lcsr-notes-file))))

  (defun jf-lcsr-log-clear ()
    (interactive)
    (when (file-exists-p jf-lcsr-today-file)
      (delete-file jf-lcsr-today-file))
    (when (file-exists-p jf-lcsr-next-file)
      (delete-file jf-lcsr-next-file))
    (when (file-exists-p jf-lcsr-notes-file)
      (delete-file jf-lcsr-notes-file)))

  (defun jf-lcsr-log (file msg)
    (append-to-file (concat "- " msg "\n") nil file))

  (defun jf-lcsr-log-now (msg)
    (interactive "sToday: ")
    (jf-lcsr-log-ensure)
    (jf-lcsr-log jf-lcsr-today-file msg))

  (defun jf-lcsr-log-next (msg)
    (interactive "sNext: ")
    (jf-lcsr-log-ensure)
    (jf-lcsr-log jf-lcsr-next-file msg))

  (defun jf-lcsr-log-note (msg)
    (interactive "sNote: ")
    (jf-lcsr-log-ensure)
    (jf-lcsr-log jf-lcsr-notes-file msg))

  (defun jf-lcsr-log-report-file ()
    (expand-file-name (concat "report-" (format-time-string "%Y-%m-%d") ".org")
                      "lcsr"))

  (defun jf-lcsr-hours ()
    (concat
     "\n"
     (pcase (format-time-string "%a")
       ("Mon" "In:  09:00\nOut: 13:00\nIn:  15:00\nOut: 19:00")
       ("Tue" "In:  09:00\nOut: 19:00")
       ("Wed" "In:  15:00\nOut: 19:00")
       ("Thu" "In:  09:00\nOut: 19:00")
       ("Fri" "In:  11:30\nOut: 15:00\nIn:  17:00\nOut: 19:00"))
     "\n\n"))

  (defun jf-lcsr-log-read-file (file)
    (with-temp-buffer
      (insert-file-contents-literally file)
      (buffer-string)))

  (defun jf-lcsr-log-buffer ()
    (let ((buffer (create-file-buffer (jf-lcsr-log-report-file))))
      (with-current-buffer buffer
        (org-mode)
        (org-time-stamp-inactive)
        (insert
         (concat
          (jf-lcsr-hours)
          "* What I did today:\n"
          (jf-lcsr-log-read-file jf-lcsr-today-file)
          "\n* What I will do next:\n"
          (jf-lcsr-log-read-file jf-lcsr-next-file)
          ;; TODO: Only include notes if there are any
          "\n* Notes:\n"
          (jf-lcsr-log-read-file jf-lcsr-notes-file)))
        (pop-to-buffer buffer))))

  (defun jf-lcsr-log-finalize ()
    (interactive)
    (jf-lcsr-log-buffer)
    ;(jf-lcsr-log-clear)
    )
Transient
  (define-transient-command jf-lcsr-issue-dispatch ()
    ["Issues"
     ("i" "Create Issue" jf-lcsr-issue-new)]
    ["Navigation"
     ("b b" "Checkout Feature <-> User" jf-lcsr-checkout-toggle)
     ("b i" "Checkout ID" jf-lcsr-checkout-id)
     ("b t" "Checkout Tag" jf-lcsr-checkout-tag)]
    ["Commits"
     ("c" "Commit with Tag" jf-lcsr-commit)
     ("m t" "Merge Feature <-> User" jf-lcsr-merge-toggle)
     ("m b" "Merge User -> Feature" jf-lcsr-mergeback)
     ("m m" "Merge" jf-lcsr-merge)]))

Forge

Magic GitHub facilities for git forges such as GitHub and GitLab!

  (use-package forge
    :after magit)

Smeargle

Highlights regions in files by last update time. Older regions are more whitey and newer regions are more blacky.

  (use-package smeargle
    :general
    (jf-git-def
      "H t" 'smeargle
      "H h" 'smeargle-commits
      "H c" 'smeargle-clear))

Projects

Projectile

Projectile provides project-level features like make shortcuts and file switching

  (use-package projectile
    :defer t
    :preface
    (defvar jf-projects-path "~/Documents/dev")

    :general
    (jf-leader-def
     "p" '(:keymap projectile-command-map))

    :config
    (projectile-mode 1)
    ;; Discover projects in jf-projects-path
    (let ((subdirs (directory-files jf-projects-path t)))
      (dolist (dir subdirs)
        (unless (member (file-name-nondirectory dir) '(".." "."))
          (when (file-directory-p dir)
            (let ((default-directory dir)
                  (projectile-cached-project-root dir))
              (when (projectile-project-p)
                (projectile-add-known-project (projectile-project-root))))))))

    :custom
    (projectile-completion-system 'ivy)
    (projectile-project-search-path (list jf-projects-path)))

Projectile ibuffer

Groups buffers in ibuffer by their project root directory.

  (use-package ibuffer-projectile
    :preface
    (defun jf-ibuffer-hook ()
      (ibuffer-projectile-set-filter-groups)
      (unless (eq ibuffer-sorting-mode 'alphabetic)
        (ibuffer-do-sort-by-alphabetic)))
    :hook (ibuffer . #'jf-ibuffer-hook))

Languages

Language Server Protocol

Mode for integration with lots of language servers, interacting with company, flycheck, projectile, and the works.

  (use-package lsp-mode
    :commands lsp)

  (use-package lsp-ui
    :commands lsp-ui-mode)

  (use-package company-lsp
    :commands company-lsp)

Fish

Mode for editing of scripts for the fish shell.

  (use-package fish-mode
    :mode "\\.fish\\'")

Markdown

Mode for editing markdown.

  (use-package markdown-mode
    :mode "\\.md\\'")

Python

Jedi

Auto completion in python-mode.

  (use-package company-jedi
    :company python-mode)
ob-ipython

Allows embedding ipython in org mode files. See the github for tips, tricks, and tutorials.

  (use-package ob-ipython)

Javascript

rjsx-mode includes a javascript mode with support for react jsx files.

  (use-package rjsx-mode
    :pin melpa
    :mode "\\.js\\'"
    :interpreter "node")

Web

Web-mode should give everything you need for a web-dev major mode. Company integration is done with company-web.

  (use-package web-mode
    :mode ("\\.html\\'"
           "\\.php\\'"
           "\\.blade\\.")
    :custom
    (web-mode-code-indent-offset 4)
    (web-mode-indent-style 4))

  (use-package company-web
    :company web-mode)

JSON

Mode for editing JSON files.

  (use-package json-mode
    :mode "\\.json\\'")

YAML

Mode for editing YAML files.

  (use-package yaml-mode
    :mode "\\.yaml\\'")

Arch PKGBUILD

Mode for editing PKGBUILD files.

  (use-package pkgbuild-mode
    :mode ".*PKGBUILD\\'")

LaTeX

AUCTeX

AUCTeX is a major mode for editing TeX. Company completions are handled by company-auctex and company-math.

  (use-package tex
    :defer t
    :ensure auctex
    :general
    (jf-major-def
     :keymaps 'TeX-mode-map
     "e" 'TeX-command-run-all)

    :custom
    (TeX-auto-save t))

  (use-package company-auctex
    :company LaTeX-mode)

  (use-package company-math
    :company ((TeX-mode . company-math-symbols-latex)
              (TeX-mode . company-math-symbols-unicode)
              (TeX-mode . company-latex-commands)))
Cdlatex

cdlatex adds better TeX-specific template expansions and other niceties.

Environment
  (setq jf-cdlatex-envs nil)
Commands
  (setq jf-cdlatex-commands nil)
Math Symbols
  (setq jf-cdlatex-symbols
        '((?I ("\\infty"))))
Setup
  (use-package cdlatex
    :hook (LaTeX-mode . cdlatex-mode)
    :preface
    (defun jf-larger-latex ()
      (interactive)
      (setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)))
    :custom
    (cdlatex-env-alist jf-cdlatex-envs)
    (cdlatex-command-alist jf-cdlatex-commands)
    (cdlatex-math-symbol-alist jf-cdlatex-symbols))

Rust

  (use-package rust-mode
    :mode "\\.rs\\'"
    :general)

  (use-package flycheck-rust
    :after flycheck
    :hook (rust-mode . flycheck-rust-setup))

  (use-package racer
    :hook ((rust-mode . racer-mode)
           (rust-mode . eldoc-mode))
    :custom
    (racer-cmd "~/.cargo/bin/racer")
    (racer-rust-src-path "~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src"))

  (use-package cargo
    :hook (rust-mode . cargo-minor-mode)
    :general
    (jf-major-def
     :keymaps 'rust-mode-map
     "b b" #'cargo-process-build
     "b r" #'cargo-process-run
     "b t" #'cargo-process-test))

C/C++

Irony

Irony handles enhanced C/C++ operations powered by clang company-irony for company integration

  (use-package irony
    :hook ((c-mode c++-mode) . irony-mode)
           (irony-mode . irony-cdb-autosetup-compile-options))

  (use-package flycheck-irony
    :after flycheck
    :hook (irony-mode . flycheck-irony-setup))

  (use-package company-irony
    :company irony-mode)

  (use-package company-irony-c-headers
    :company irony-mode)

Lua

  (use-package lua-mode
    :mode "\\.lua\\'")

  (use-package company-lua
    :company lua-mode)

Elixir

  (use-package elixir-mode
      :mode "\\.exs?\\'"
      :hook (elixir-mode . lsp))

Java

Eclim

Secretly actually use eclipse in the background in the form of eclimd for all of the IDE like features.

  (use-package eclim
    :hook ((java-mode . eclim-mode)
           (java-mode . jf-indent-4-spaces))
    :custom
    (eclimd-default-workspace "~/Documents/dev/workspace")
    (eclimd-autostart-with-default-workspace t)
    (eclimd-autostart t))

  (use-package company-emacs-eclim
    :company java-mode)
Gradle

The std:: java build system

  (use-package gradle-mode
    :commands (gradle-mode)
    :preface
    (defun jf-gradle-run-main ()
      (gradle-execute "run"))
    :general
    (jf-major-def
      :keymaps 'gradle-mode-map
      "b b" #'gradle-build
      "b r" #'jf-gradle-run-main))

GLSL

Support for editing GLSL shader files.

  (use-package glsl-mode
    :mode ("\\.vert\\'"
           "\\.frag\\'"
           "\\.comp\\'"
           "\\.glsl\\'"))

  (use-package company-glsl
    :company glsl-mode)

Hoon

Mode for the Urbit programming language.

  ;;(add-hook 'hoon-mode
  ;;          (lambda ()
  ;;            (define-key hoon-mode-map (kbd "C-c r") 'hoon-eval-region-in-urb)
  ;;            (define-key hoon-mode-map (kbd "C-c b") 'hoon-eval-buffer-in-urb))
  ;;            (jf-indent-spaces 2))