* Packaging ** Package.el [[http://wikemacs.org/wiki/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. #+BEGIN_SRC emacs-lisp (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) #+END_SRC ** use-package [[https://github.com/jwiegley/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. #+BEGIN_SRC emacs-lisp (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) #+END_SRC *** 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. #+BEGIN_SRC emacs-lisp (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 " or " "( . ) 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)))) #+END_SRC * Keybind Helpers ** which-key [[https://github.com/justbur/emacs-which-key][which-key]] displays a popup in the minibuffer that shows and describes keybindings prefixed by incomplete commands. #+BEGIN_SRC emacs-lisp (use-package which-key :diminish :config (which-key-mode)) #+END_SRC ** General [[https://github.com/noctuid/general.el][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. #+BEGIN_SRC emacs-lisp (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-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 visual insert emacs) :prefix ,(concat "SPC " key) :non-normal-prefix ,(concat "M-SPC " key)) (jf-leader-def ,key ',(jf-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")))) #+END_SRC ** Macros *** jf-kbd Helper functions for defining procedural keyboard macros #+BEGIN_SRC emacs-lisp (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" . "") ("DEL" . "") ("BS" . "") ("RET" . "") ("SPC" . "") ("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))) #+END_SRC * Vim Emulation ** Evil [[https://github.com/emacs-evil/evil][Evil]] is pretty much the entirety of Vim in Emacs. Without it I would probably still be using vim. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ** Evil Collection [[https://github.com/emacs-evil/evil-collection][Evil Collection]] adds Evil bindings for all the parts of Emacs that Evil doesn't cover properly by default. #+BEGIN_SRC emacs-lisp (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))) #+END_SRC ** Evil Extensions *** Avy An enhanced version of =f= in Vim. #+BEGIN_SRC emacs-lisp (use-package avy :general (:keymaps 'override :states 'normal "C-f" 'avy-goto-char-in-line "C-F" 'avy-goto-char)) #+END_SRC *** 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. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** evil-surround Bind =S= and a delimiter to surround in visual mode. #+BEGIN_SRC emacs-lisp (use-package evil-surround :after evil :diminish :config (global-evil-surround-mode 1)) #+END_SRC *** evil-goggles Flash highlight on evil motions for better visual feedback of what's happening. #+BEGIN_SRC emacs-lisp (use-package evil-goggles :config (evil-goggles-mode) (evil-goggles-use-diff-faces)) #+END_SRC *** evil-matchit #+BEGIN_SRC emacs-lisp (use-package evil-matchit :after evil :config (global-evil-matchit-mode 1)) #+END_SRC * 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. #+BEGIN_SRC emacs-lisp (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))) #+END_SRC *** File Not Found Functions Offer to create nonexistant parent folders when a new file is opened #+BEGIN_SRC emacs-lisp (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) #+END_SRC *** Customize Location Make changes in =M-x customize= go somewhere other than being schlunked into =init.el=. #+BEGIN_SRC emacs-lisp (setq custom-file (concat user-emacs-directory "customize.el")) (load custom-file t) #+END_SRC *** Disable Bell Shut up, emacs. #+BEGIN_SRC emacs-lisp (setq ring-bell-function #'ignore) #+END_SRC *** Shorter Prompts Make =yes-or-no= prompts ask for =y-or-n= instead. Saves loads of time™. #+BEGIN_SRC emacs-lisp (defalias 'yes-or-no-p #'y-or-n-p) #+END_SRC *** Move Backup Files By default, emacs gunks up every folder with =file~= backups and =#file#= lockfiles. Schlunk them all in =.emacs.d/saves= instead. #+BEGIN_SRC emacs-lisp (let ((save-dir (locate-user-emacs-file "saves"))) (setq backup-directory-alist `((".*" . ,save-dir))) (setq auto-save-file-name-transforms `((".*" ,(concat save-dir "/") t)))) #+END_SRC *** 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. #+BEGIN_SRC emacs-lisp (setq create-lockfiles nil) #+END_SRC *** Secure auth-source GPG encrypt stored auth tokens from [[https://www.gnu.org/software/emacs/manual/html_mono/auth.html][auth-source]] instead of storing them in plaintext. #+BEGIN_SRC emacs-lisp (setq auth-sources '("~/.emacs.d/authinfo.gpg")) #+END_SRC *** Use UTF-8 Pleeeease default to UTF-8, Emacs. #+BEGIN_SRC emacs-lisp (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) #+END_SRC *** Trash when Deleting Don't permanently delete stuff unless asked. #+BEGIN_SRC emacs-lisp (setq delete-by-moving-to-trash t) #+END_SRC *** Open Compressed Files ...automatically. #+BEGIN_SRC emacs-lisp (setq auto-compression-mode t) #+END_SRC *** Save Minibuffer History #+BEGIN_SRC emacs-lisp (savehist-mode 1) (setq history-length 1000) #+END_SRC *** Double Spaces Why sentences would need double spaces to end I do not know. #+BEGIN_SRC emacs-lisp (set-default 'sentence-end-double-space nil) #+END_SRC *** Eval Print Level Print more stuff when running =C-x C-e= or =(eval-last-sexp)= #+BEGIN_SRC emacs-lisp (setq eval-expression-print-level 100) #+END_SRC *** 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. #+BEGIN_SRC emacs-lisp (add-hook 'minibuffer-setup-hook #'jf-inhibit-gc) (add-hook 'minibuffer-exit-hook #'jf-resume-gc) #+END_SRC *** Default Shell I use fish and it breaks some things when used as the default shell, so just explicitly tell emacs to use bash. #+BEGIN_SRC emacs-lisp (setq-default shell-file-name "/bin/bash") #+END_SRC *** Scratch buffer message #+BEGIN_SRC emacs-lisp (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.") #+END_SRC ** UI *** Font Set up a nice coding font with ligatures. #+BEGIN_SRC emacs-lisp (add-to-list 'default-frame-alist '(font . "Fira Code 12")) (set-face-attribute 'default t :font "Fira Code 12") #+END_SRC *** Menu Bar Disable the useless cruft at the top of the screen. #+BEGIN_SRC emacs-lisp (menu-bar-mode -1) (tool-bar-mode -1) (scroll-bar-mode -1) #+END_SRC *** Modeline **** Diminish Adds support for =:diminish= in use-package declarations, which hides a mode from the modeline. #+BEGIN_SRC emacs-lisp (use-package diminish) #+END_SRC **** Column Number Show line and column numbers in the modeline. #+BEGIN_SRC emacs-lisp (setq line-number-mode t) (setq column-number-mode t) #+END_SRC *** Line Numbers Use the default emacs relative line numbers, but switch to absolute lines when in insert mode. #+BEGIN_SRC emacs-lisp (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)))) #+END_SRC *** Show Matching Parens Highlights matching parenthesis when the point is near one #+BEGIN_SRC emacs-lisp (require 'paren) (setq show-paren-delay 0) (show-paren-mode) #+END_SRC *** Scrolling Scroll smooth-ish-ly instead of jarring jumps. #+BEGIN_SRC emacs-lisp ;;(use-package smooth-scroll ;; :config ;; (smooth-scroll-mode t)) #+END_SRC *** Dashboard Show a cool custom dashboard buffer on startup. #+BEGIN_SRC emacs-lisp (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)))) #+END_SRC ** Themes *** pywal Fancy dynamic color scheme generation from desktop wallpapers. Requires additional setup per-machine. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** spacemacs A pretty fancy theme that has lots of supported modes. #+BEGIN_SRC emacs-lisp (unless (package-installed-p 'spacemacs-theme) (package-install 'spacemacs-theme)) (defun jf-theme-spacemacs () (load-theme 'spacemacs-dark)) #+END_SRC *** 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. #+BEGIN_SRC emacs-lisp (set-frame-parameter (selected-frame) 'alpha 85) (add-to-list 'default-frame-alist '(alpha . 85)) #+END_SRC *** Helpers Keep track of the current theme and apply it at startup #+BEGIN_SRC emacs-lisp (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) #+END_SRC ** Keybinds *** Files Define and bind functions for managing files, including emacs config files. #+BEGIN_SRC emacs-lisp (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-wk-prefix "emacs files") "ec" #'jf-edit-config "ei" #'jf-edit-init "er" #'jf-reload-config "eb" #'jf-byte-compile-config) #+END_SRC *** Buffers Define and bind some functions for managing buffers. #+BEGIN_SRC emacs-lisp (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) #+END_SRC * Organization ** Capture Templates All capture templates, from tasks to bookmarks. Capture dispatch is invoked with `SPC o c`. *** Tasks #+BEGIN_SRC emacs-lisp (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"))) #+END_SRC *** Bookmarks #+BEGIN_SRC emacs-lisp (setq jf-org-capture-bookmark-templates '(("b" "Bookmark" entry (file "refile.org") "** [[%^{link}][%^{name}]] :LINK:\n%U\n%?"))) #+END_SRC *** Personal #+BEGIN_SRC emacs-lisp (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%?"))) #+END_SRC *** Protocol #+BEGIN_SRC emacs-lisp (setq jf-org-capture-protocol-templates '(("w" "Website [Protocol]" entry (file "refile.org") "** [[%:link][%:description%?]] :LINK:\n%U\n%a\n%i\n%?"))) #+END_SRC *** All Tie it all together. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** Hooks Temporarily widens the capture mode buffer and loads the tag completion alist with the full buffer tags. #+BEGIN_SRC emacs-lisp (defun jf-update-capture-tags () (save-restriction (widen) (setq-local org-tag-alist (org-get-buffer-tags)))) #+END_SRC ** Structure Templates Defines expansions with =<= followed by a string in org-mode. For example, = NEXT -> DONE, with some exceptions like WAITING and CANCELLED. #+BEGIN_SRC emacs-lisp (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))) #+END_SRC *** 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 #+BEGIN_SRC emacs-lisp (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"))))) #+END_SRC ** Evil org Spice up org mode with some deliciously evil bindings. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ** Pretty bullets Make bullets indent content and look choice #+BEGIN_SRC emacs-lisp (use-package org-bullets :after org :hook (org-mode . org-bullets-mode)) #+END_SRC ** Org Keep org-mode up to date straight from the cow's utters. If the manual is not on your computer, it's [[https://orgmode.org/manual/][here]]. #+BEGIN_SRC emacs-lisp (use-package org :defer t :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")) #+END_SRC * Communication * Web * Tools ** Fuzzy Matching Most facilities are provided by [[https://github.com/abo-abo/swiper][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. #+BEGIN_SRC emacs-lisp (use-package smex) #+END_SRC *** Ivy [[https://github.com/abo-abo/swiper][Ivy]] has most of the buffer-related commands. #+BEGIN_SRC emacs-lisp (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")) #+END_SRC *** Counsel A collection of ivy enhanced versions of common Emacs commands. Bind up some useful ones. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** Swiper An ivy-ified replacement for isearch. #+BEGIN_SRC emacs-lisp (use-package swiper :after evil :general (:keymaps 'override :states 'normal "/" 'swiper "n" 'evil-search-previous "N" 'evil-search-next)) #+END_SRC ** Neotree A cool toggleable directory structure sidebar. It needs icon fonts, installed with =M-x all-the-icons-install-fonts=. #+BEGIN_SRC emacs-lisp (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))) #+END_SRC ** Ranger Brings the glory of [[https://github.com/ralesi/ranger.el][Ranger]] to Emacs. #+BEGIN_SRC emacs-lisp (use-package ranger :commands (ranger deer)) #+END_SRC ** Sunshine Allows retrieving OpenWeatherMap forecasts in the minibuffer. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ** Kubernetes A porcelain like magit for kubernetes. #+BEGIN_SRC emacs-lisp ;;(use-package kubernetes ;; :commands kubernetes-overview) ;;(use-package kubernetes-evil ;; :after kubernetes) #+END_SRC * Programming ** Formatting *** Word Wrap Add a command for setting the word wrap column. When enabled, use =M-q= to wrap the current block. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** Indentation How code should be 'dented. **** Defaults Set some *sane* defaults for indentation. #+BEGIN_SRC emacs-lisp (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) #+END_SRC **** Helpers Define some useful helper functions related to indentation used later in the config. #+BEGIN_SRC emacs-lisp (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) #+END_SRC *** Parentheses **** Smartparens [[https://github.com/Fuco1/smartparens][Smartparens]] handles electric parens and navigation for languages that aren't lispy. #+BEGIN_SRC emacs-lisp (use-package smartparens :diminish :commands smartparens-mode :config (require 'smartparens-config)) #+END_SRC **** ParEdit And [[https://www.emacswiki.org/emacs/ParEdit][ParEdit]] handles the rest. #+BEGIN_SRC emacs-lisp (use-package paredit :diminish :commands enable-paredit-mode) #+END_SRC **** Lispyville And now Lispyville handles the rest. #+BEGIN_SRC emacs-lisp (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)))) #+END_SRC **** Evil-Cleverparens [[https://github.com/luxbock/evil-cleverparens][Evil-Cleverparens]] adds additional features to Evil all about working with sexps, including keeping parens balanced when using commands like =dd=. #+BEGIN_SRC emacs-lisp (use-package evil-cleverparens :diminish :commands evil-cleverparens-mode) #+END_SRC **** Activation Add a hook to enable paren helper modes for any prog-mode buffer #+BEGIN_SRC emacs-lisp (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) #+END_SRC **** Helpers Helpers for wrangling sexps #+BEGIN_SRC emacs-lisp (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^") #+END_SRC *** Whitespace **** ws-butler Unobtrusively cleans up whitespace before EOLs as you edit, stopping the noisy commits generated from blanket trimming entire files. #+BEGIN_SRC emacs-lisp (use-package ws-butler :hook (prog-mode . ws-butler-mode)) #+END_SRC *** pretty-mode [[https://github.com/pretty-mode/pretty-mode][pretty-mode]] redisplays parts of the Emacs buffer as pretty symbols. #+BEGIN_SRC emacs-lisp (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))) #+END_SRC *** 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 [[https://en.wikipedia.org/wiki/Mathematical_operators_and_symbols_in_Unicode][here]]. **** Python #+BEGIN_SRC emacs-lisp (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) #+END_SRC *** Pretty fonts Enables ligatures from fira-code to be used with prettify-symbols-mode. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** Rainbow Delimiters Make it easier to spot sexp depth by pairing matching delimiters with rainbow colors. #+BEGIN_SRC emacs-lisp (use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode)) #+END_SRC ** Checkers *** Flycheck Flycheck highlights syntax errors in a few languages. #+BEGIN_SRC emacs-lisp (use-package flycheck :hook (prog-mode . flycheck-mode) :custom (flycheck-disabled-checkers '(emacs-lisp-checkdoc))) #+END_SRC *** Column 80 Highlight Add a hotkey for highlighting column 80 in prog-mode #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ** Completion *** Company Company auto-completes stuff in the buffer, and company-quickhelp shows documentation popups when idling on a completion candidate. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ** Snippets Yasnippet adds support for custom snippets, invoked by inputting a prefix and pressing =TAB=. #+BEGIN_SRC emacs-lisp (use-package yasnippet :hook (prog-mode . yas-minor-mode) :custom (yas-snippet-dirs '("~/.emacs.d/snippets" "~/.emacs.d/elpa/yasnippet-snippets-0.6/snippets"))) #+END_SRC ** Debugging *** Realgud [[https://github.com/realgud/realgud][Realgud]] is a modular frontend for many debuggers, right in Emacs. #+BEGIN_SRC emacs-lisp (use-package realgud :commands (realgud:gdb realgud:lldb realgud:node-inspect realgud:pdb realgud:trepan3k)) #+END_SRC *** RMSbolt [[https://github.com/emacsmirror/rmsbolt][RMSbolt]] Shows disassembly in a buffer next to code, highlighting relevant regions. #+BEGIN_SRC emacs-lisp (use-package rmsbolt :commands rmsbolt-mode) #+END_SRC ** Git *** Magit **** Core It's magic git! Keybinds [[https://github.com/emacs-evil/evil-magit][here]] #+BEGIN_SRC emacs-lisp (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-issue-init)) #+END_SRC It's *evil* magic git! #+BEGIN_SRC emacs-lisp (use-package evil-magit :after (evil magit)) #+END_SRC **** 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 #+BEGIN_SRC emacs-lisp (defvar jf-lcsr-me "jwf78") #+END_SRC ***** Predicates #+BEGIN_SRC emacs-lisp (defun jf-lcsr-branch-base-p (branch) (string-match "^\\(origin/\\)?issue/[0-9]\\{1,4\\}/[A-Za-z-]+$" branch)) (defun jf-lcsr-branch-user-p (branch) (string-match "^\\(origin/\\)?issue/[0-9]\\{1,4\\}/[a-z]\\{2,3\\}[0-9]\\{1,5\\}$" branch)) (defun jf-lcsr-branch-origin-p (branch) (string-match "^origin/issue/[0-9]\\{1,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))) #+END_SRC ***** Splitting/Joining #+BEGIN_SRC emacs-lisp (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)))) #+END_SRC ***** Listing/Searching #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ***** Navigation #+BEGIN_SRC emacs-lisp (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)))) #+END_SRC ***** Commits #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ***** Issues #+BEGIN_SRC emacs-lisp (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))) #+END_SRC ***** Tracking #+BEGIN_SRC emacs-lisp (defun jf-lcsr-log-file (&optional file) (if file (expand-file-name (concat (file-name-as-directory "lcsr") file) user-emacs-directory) (expand-file-name (file-name-as-directory "lcsr") user-emacs-directory))) (defvar jf-lcsr-dir (jf-lcsr-log-file)) (defvar jf-lcsr-today-file (jf-lcsr-log-file "today.org")) (defvar jf-lcsr-next-file (jf-lcsr-log-file "next.org")) (defvar jf-lcsr-notes-file (jf-lcsr-log-file "notes.org")) (defvar jf-lcsr-files (list jf-lcsr-today-file jf-lcsr-next-file jf-lcsr-notes-file)) (defun jf-lcsr-log-ensure () (unless (file-exists-p jf-lcsr-dir) (make-directory jf-lcsr-dir)) (dolist (f jf-lcsr-files) (unless (file-exists-p f) (with-temp-buffer (write-file f))))) (defun jf-lcsr-log-clear () (interactive) (dolist (f jf-lcsr-files) (when (file-exists-p f) (delete-file f)))) (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-format-hours (times) (seq-reduce (lambda (acc p) (concat acc (let ((start (car p)) (end (cadr p))) (format "[%02d:%02d]--[%02d:%02d] => %02d:%02d\n" (/ start 100) (mod start 100) (/ end 100) (mod end 100) (if (> (mod start 100) 0) (- (- (/ end 100) (/ start 100)) 1) (- (/ end 100) (/ start 100))) (if (> (mod start 100) 0) 30 0))))) times "")) (defun jf-lcsr-hours () (concat "\n" (pcase (format-time-string "%a") ("Mon" (jf-lcsr-format-hours '((0900 1300) (1500 1900)))) ("Tue" (jf-lcsr-format-hours '((0900 1900)))) ("Wed" (jf-lcsr-format-hours '((1500 1900)))) ("Thu" (jf-lcsr-format-hours '((0900 1900)))) ("Fri" (jf-lcsr-format-hours '((1130 1500) (1700 1900))))) "\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) (unless (with-temp-buffer (insert-file-contents-literally jf-lcsr-notes-file) (eq (buffer-size) 0)) (concat "\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) ) #+END_SRC ***** Transient #+BEGIN_SRC emacs-lisp (defun jf-lcsr-issue-init () (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)])) #+END_SRC *** Forge Magic GitHub facilities for git forges such as GitHub and GitLab! #+BEGIN_SRC emacs-lisp (use-package forge :after magit) #+END_SRC *** Smeargle Highlights regions in files by last update time. Older regions are more whitey and newer regions are more blacky. #+BEGIN_SRC emacs-lisp (use-package smeargle :general (jf-git-def "H t" 'smeargle "H h" 'smeargle-commits "H c" 'smeargle-clear)) #+END_SRC ** Projects *** Projectile Projectile provides project-level features like make shortcuts and file switching #+BEGIN_SRC emacs-lisp (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-enable-caching t) (projectile-completion-system 'ivy) (projectile-project-search-path (list jf-projects-path)) (projectile-globally-ignored-file-suffixes '("#" "~" ".swp" ".o" ".so" ".a" ".elc" ".pyc" ".jar")) (projectile-globally-ignored-directories '(".git" "node_modules" "__pycache__")) (projectile-globally-ignored-files '("TAGS" "tags" ".DS_Store"))) #+END_SRC *** Projectile ibuffer Groups buffers in ibuffer by their project root directory. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ** Languages *** Language Server Protocol Mode for integration with lots of language servers, interacting with company, flycheck, projectile, and the works. #+BEGIN_SRC emacs-lisp (use-package lsp-mode :commands lsp) (use-package lsp-ui :commands lsp-ui-mode) (use-package company-lsp :commands company-lsp) #+END_SRC *** Fish Mode for editing of scripts for the fish shell. #+BEGIN_SRC emacs-lisp (use-package fish-mode :mode "\\.fish\\'") #+END_SRC *** Markdown Mode for editing markdown. #+BEGIN_SRC emacs-lisp (use-package markdown-mode :mode "\\.md\\'") #+END_SRC *** Python **** Jedi Auto completion in python-mode. #+BEGIN_SRC emacs-lisp (use-package company-jedi :company python-mode) #+END_SRC **** ob-ipython Allows embedding ipython in org mode files. See the [[https://github.com/gregsexton/ob-ipython][github]] for tips, tricks, and tutorials. #+BEGIN_SRC emacs-lisp (use-package ob-ipython :after org) #+END_SRC *** Javascript **** rjsx-mode rjsx-mode includes a javascript mode with support for react jsx files. #+BEGIN_SRC emacs-lisp (use-package rjsx-mode :pin melpa :mode "\\.js\\'" :interpreter "node" :config (setq-default js2-basic-offset 4) :general (jf-major-def :keymaps 'rjsx-mode-map "r" (jf-wk-prefix "refactor") "h" (jf-wk-prefix "documentation") "g" (jf-wk-prefix "goto") "z" (jf-wk-prefix "folding") "w" 'js2-mode-toggle-warnings-and-errors "zc" 'js2-mode-hide-element "zo" 'js2-mode-show-element "zr" 'js2-mode-show-all "ze" 'js2-mode-toggle-element "zF" 'js2-mode-toggle-hide-functions "zC" 'js2-mode-toggle-hide-comments)) #+END_SRC **** tern Tern is like a language server for javascript. #+BEGIN_SRC emacs-lisp (use-package tern :hook (js2-mode . tern-mode) :general (jf-major-def :keymaps 'tern-mode-keymap "rr" (jf-wk-prefix "rename") "rrv" #'tern-rename-variable "hd" #'tern-get-docs "ht" #'tern-get-type "g/" #'tern-find-definition-by-name "C-g" #'tern-pop-find-definition)) (use-package company-tern :company rjsx-mode :custom (company-tern-meta-as-single-line t)) #+END_SRC **** js2-refactor js2-refactor adds all kinds of source code transformation shortcuts for javascript. #+BEGIN_SRC emacs-lisp (use-package js2-refactor :defer t :preface (defun jf-load-js2-refactor () (require 'js2-refactor)) :hook (js2-mode . jf-load-js2-refactor) :general (jf-major-def :keymaps 'js2-mode-map "r3" (jf-wk-prefix "ternary") "r3i" #'js2r-ternary-to-if "ra" (jf-wk-prefix "add/args") "rag" #'js2r-add-to-globals-annotation "rao" #'js2r-arguments-to-object "rb" (jf-wk-prefix "barf") "rba" #'js2r-forward-barf "rc" (jf-wk-prefix "contract") "rca" #'js2r-contract-array "rco" #'js2r-contract-object "rcu" #'js2r-contract-function "re" (jf-wk-prefix "expand/extract") "rea" #'js2r-expand-array "ref" #'js2r-extract-function "rem" #'js2r-extract-method "reo" #'js2r-expand-object "reu" #'js2r-expand-function "rev" #'js2r-extract-var "ri" (jf-wk-prefix "inline/inject/introduce") "rig" #'js2r-inject-global-in-iife "rip" #'js2r-introduce-parameter "riv" #'js2r-inline-var "rl" (jf-wk-prefix "localize/log") "rlp" #'js2r-localize-parameter "rlt" #'js2r-log-this "rs" (jf-wk-prefix "split/slurp") "rsl" #'js2r-forward-slurp "rss" #'js2r-split-string "rsv" #'js2r-split-var-declaration "rt" (jf-wk-prefix "toggle") "rtf" #'js2r-toggle-function-expression-and-declaration "ru" (jf-wk-prefix "unwrap") "ruw" #'js2r-unwrap "rv" (jf-wk-prefix "var") "rvt" #'js2r-var-to-this "rw" (jf-wk-prefix "wrap") "rwi" #'js2r-wrap-buffer-in-iife "rwl" #'js2r-wrap-in-for-loop "x" (jf-wk-prefix "text") "xm" (jf-wk-prefix "move") "xmj" #'js2r-move-line-down "xmk" #'js2r-move-line-up "k" #'js2r-kill)) #+END_SRC **** js-doc js2-doc adds helpers for reading and writing documentation for javascript code. #+BEGIN_SRC emacs-lisp (use-package js-doc :defer t :preface (defun jf-load-js-doc () (require 'js-doc)) :hook (js2-mode . jf-load-js-doc) :general (jf-major-def :keymaps 'js2-mode-map "rdb" #'js-doc-insert-file-doc "rdf" #'js-doc-insert-function-doc "rdt" #'js-doc-insert-tag "rdh" #'js-doc-describe-tag)) #+END_SRC *** Web Web-mode should give everything you need for a web-dev major mode. Company integration is done with company-web. #+BEGIN_SRC emacs-lisp (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) #+END_SRC *** JSON Mode for editing JSON files. #+BEGIN_SRC emacs-lisp (use-package json-mode :mode "\\.json\\'") #+END_SRC *** YAML Mode for editing YAML files. #+BEGIN_SRC emacs-lisp (use-package yaml-mode :mode "\\.yaml\\'") #+END_SRC *** Arch PKGBUILD Mode for editing PKGBUILD files. #+BEGIN_SRC emacs-lisp (use-package pkgbuild-mode :mode ".*PKGBUILD\\'") #+END_SRC *** LaTeX **** AUCTeX AUCTeX is a major mode for editing TeX. Company completions are handled by company-auctex and company-math. #+BEGIN_SRC emacs-lisp (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))) #+END_SRC **** Cdlatex cdlatex adds better TeX-specific template expansions and other niceties. ***** Environment #+BEGIN_SRC emacs-lisp (setq jf-cdlatex-envs nil) #+END_SRC ***** Commands #+BEGIN_SRC emacs-lisp (setq jf-cdlatex-commands '(("ch" "Insert n choose r" "\\left( \\begin{array}{c} {?} \\\\ {} \\end{array} \\right)" cdlatex-position-cursor nil nil t))) #+END_SRC ***** Math Symbols #+BEGIN_SRC emacs-lisp (setq jf-cdlatex-symbols '((?I ("\\infty")))) #+END_SRC ***** Setup #+BEGIN_SRC emacs-lisp (use-package cdlatex :preface (defun jf-larger-latex () (interactive) (setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5))) :hook ((LaTeX-mode . cdlatex-mode) (org-cdlatex-mode . jf-larger-latex)) :custom (cdlatex-env-alist jf-cdlatex-envs) (cdlatex-command-alist jf-cdlatex-commands) (cdlatex-math-symbol-alist jf-cdlatex-symbols)) #+END_SRC *** Rust #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** C/C++ **** Irony Irony handles enhanced C/C++ operations powered by clang company-irony for company integration #+BEGIN_SRC emacs-lisp (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) #+END_SRC *** Lua #+BEGIN_SRC emacs-lisp (use-package lua-mode :mode "\\.lua\\'") (use-package company-lua :company lua-mode) #+END_SRC *** Elixir #+BEGIN_SRC emacs-lisp (use-package elixir-mode :mode "\\.exs?\\'" :hook (elixir-mode . lsp)) #+END_SRC *** Java **** Eclim Secretly actually use eclipse in the background in the form of eclimd for all of the IDE like features. #+BEGIN_SRC emacs-lisp (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) #+END_SRC **** Gradle The std:: java build system #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** GLSL Support for editing GLSL shader files. #+BEGIN_SRC emacs-lisp (use-package glsl-mode :mode ("\\.vert\\'" "\\.frag\\'" "\\.comp\\'" "\\.glsl\\'")) (use-package company-glsl :company glsl-mode) #+END_SRC *** Hoon Mode for the Urbit programming language. #+BEGIN_SRC emacs-lisp ;;(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)) #+END_SRC *** Haskell **** haskell-mode #+BEGIN_SRC emacs-lisp (use-package haskell-mode :mode "\\.hs\\'") #+END_SRC