Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/anschwa/emacs.d

My complete Emacs configuration.
https://github.com/anschwa/emacs.d

Last synced: about 2 months ago
JSON representation

My complete Emacs configuration.

Awesome Lists containing this project

README

        

#+TITLE: Emacs Org Configuration
#+OPTIONS: ':true *:true num:nil

* Description
This configuration is designed for Emacs 28 and above. This file
includes everything required to setup my environment.

If starting Emacs for the first time using this configuration, you
will need to do the following:
1. Visit this file and execute =M-x org-babel-tangle=.
2. Restart Emacs.

[[file:screenshot.png]]

* Starting Emacs
Exchange startup time for elegance:
#+BEGIN_SRC emacs-lisp :tangle init.el
;;; init.el
(require 'ob-tangle)
(org-babel-load-file (expand-file-name "readme.org" user-emacs-directory))
#+END_SRC

* Essentials
#+BEGIN_SRC emacs-lisp
;; Turn off mouse interface early in startup to avoid momentary display.
(if (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(if (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))

;; No splash screen.
(setq inhibit-startup-message t)

;; No fascists.
(setq initial-scratch-message nil)

;; No alarms.
(setq ring-bell-function 'ignore)

;; Productive default mode.
(setq initial-major-mode 'org-mode)

;; Change cursor.
(setq-default cursor-type 'box)
(blink-cursor-mode -1)

;; When on a tab, make the cursor the tab length…
(setq-default x-stretch-cursor t)

;; But never insert tabs…
(set-default 'indent-tabs-mode nil)

;; Except in Makefiles.
(add-hook 'makefile-mode-hook 'indent-tabs-mode)

;; Keep files clean.
(setq-default require-final-newline t)
(setq-default show-trailing-whitespace t)
(add-hook 'before-save-hook 'whitespace-cleanup)
#+END_SRC

* Housekeeping
#+BEGIN_SRC emacs-lisp
;; No backups or auto-save
(setq auto-save-default nil)
(setq make-backup-files nil)

;; Don't write lock-files
(setq create-lockfiles nil)

;; Keep emacs Custom-settings in separate file
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (not (file-exists-p custom-file))
(write-region "" nil custom-file))
(load custom-file)
#+END_SRC

* Better Defaults
#+BEGIN_SRC emacs-lisp
;; Line Numbers
(global-display-line-numbers-mode t)

;; Fix empty clipboard error
(setq save-interprogram-paste-before-kill nil)

;; Remove text in active region if inserting text
(delete-selection-mode 1)

;; Don't automatically copy selected text
(setq select-enable-primary nil)

;; Full path in frame title.
(setq frame-title-format '(buffer-file-name "%f" ("%b")))

;; Auto refresh buffers when edits occur outside emacs
(global-auto-revert-mode 1)

;; Also auto refresh Dired, but be quiet about it
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)

;; Quickly copy/move file in Dired
(setq dired-dwim-target t)

;; Show keystrokes in progress
(setq echo-keystrokes 0.1)

;; Move files to trash when deleting
(setq delete-by-moving-to-trash t)

;; Transparently open compressed files
(auto-compression-mode t)

;; Show matching parens
(setq show-paren-delay 0)
(show-paren-mode 1)

;; Auto-close brackets and double quotes
(electric-pair-mode 1)

;; Don't automatically indent lines
(electric-indent-mode -1)

;; Answering just 'y' or 'n' will do
(defalias 'yes-or-no-p 'y-or-n-p)

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

;; Always display line and column numbers
(setq line-number-mode t)
(setq column-number-mode t)

;; Wrap lines at 80 characters wide, not 72
(setq fill-column 80)

;; Smooth Scroll.
(setq mouse-wheel-scroll-amount '(1 ((shift) .1))) ; one line at a time

;; Scroll one line when hitting bottom of window
(setq scroll-conservatively 10000)

;; Navigate sillycased words
(global-subword-mode 1)

;; Word wrap (t is no wrap, nil is wrap)
(setq-default truncate-lines nil)

;; Sentences do not need double spaces to end. Period.
(set-default 'sentence-end-double-space nil)

;; Don't use shift to mark things.
(setq shift-select-mode nil)

;; eval-expression-print-level needs to be set to nil (turned off) so
;; that you can always see what's happening
(setq eval-expression-print-level nil)

;; Allow clipboard from outside emacs
(setq select-enable-clipboard t
save-interprogram-paste-before-kill t
apropos-do-all t
mouse-yank-at-point t)

;; Improve performance of very long lines
(setq-default bidi-display-reordering 'left-to-right)
#+END_SRC

* Package Management
#+BEGIN_SRC emacs-lisp
(require 'package)
(package-initialize)

(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)

;; Ensure use-package is installed
(when (not (package-installed-p 'use-package))
(package-refresh-contents)
(package-install 'use-package))

(eval-when-compile
(require 'use-package))
#+END_SRC

* Better Package Defaults
#+BEGIN_SRC emacs-lisp
;; Add parts of each file's directory to the buffer name if not unique
(use-package uniquify
:config
(setq uniquify-buffer-name-style 'forward))

;; Save point position between sessions
(use-package saveplace
:config
(setq-default save-place t)
(setq save-place-file (expand-file-name "places" user-emacs-directory)))

;; Fido (icomplete now includes fuzzy matching for M-x and other completions)
(use-package icomplete
:demand t
:config
(add-hook 'icomplete-minibuffer-setup-hook
(lambda () (setq-local max-mini-window-height 10)))
(fido-vertical-mode 1)
:bind (:map icomplete-fido-mode-map
("RET" . icomplete-fido-ret)
("TAB" . icomplete-force-complete)))

;; Recent Files
(use-package recentf
:config
(setq recentf-auto-cleanup 'never) ;; prevent issues with Tramp
(setq recentf-max-saved-items 100)
(setq recentf-max-menu-items 15)
(recentf-mode t)

(defun my/recentf-ido-find-file ()
"Find a recent file using ido."
(interactive)
(let ((file (completing-read "Choose recent file: " recentf-list nil t)))
(when file
(find-file file))))

:bind ("C-x f" . my/recentf-ido-find-file))
#+END_SRC

* Global Keybindings
#+BEGIN_SRC emacs-lisp
;; Exiting
;; The mnemonic is C-x REALLY QUIT
(global-set-key (kbd "C-x r q") 'save-buffers-kill-terminal)
(global-set-key (kbd "C-x C-c") 'delete-frame)

;; Dvorak
(global-set-key (kbd "C-t") ctl-x-map)
(global-set-key (kbd "M-t") 'execute-extended-command)

;; Symbol completion
(global-set-key (kbd "M-/") 'hippie-expand)

(setq tab-always-indent 'complete)
(setq hippie-expand-try-functions-list
'(try-expand-dabbrev
try-expand-dabbrev-from-kill
try-expand-dabbrev-all-buffers
try-complete-file-name
try-complete-lisp-symbol-partially
try-complete-lisp-symbol))

;; Window Navigation
(global-set-key (kbd "M-o") 'other-window)

;; Buffer Navigation
(global-set-key (kbd "C-x C-b") 'switch-to-buffer)

;; Cycle Whitespace
(global-set-key (kbd "C-c C-SPC") 'cycle-spacing)

;; Window splitting
(global-set-key (kbd "M-0") 'delete-window)
(global-set-key (kbd "M-1") 'delete-other-windows)
(global-set-key (kbd "M-2") 'split-window-vertically)
(global-set-key (kbd "M-3") 'split-window-horizontally)
(global-set-key (kbd "M-=") 'balance-windows)

;; More parity with readline
(defun my/backward-kill-word (&optional arg)
"kill active region or one word backward"
(interactive "p")
(if (region-active-p)
(kill-region (region-beginning) (region-end))
(backward-kill-word arg)))

(global-set-key (kbd "C-h") 'backward-delete-char) ; help is still available with M-x describe-*
(global-set-key (kbd "C-w") 'my/backward-kill-word)

;; Unset keys
(dolist (keys '("" "" "" ""
"s-c" "s-v" "s-x" "s-v" "s-q" "s-s" "s-w"
"s-a" "s-o" "s-n" "s-p" "s-k" "s-u" "s-m"
"s-f" "s-z" "s-g" "s-d" "s-," "s-:" "s-e"
"s-t" "C-z" "C-\\" "C-M-i"))
(global-unset-key (kbd keys)))
#+END_SRC

* Appearance
** Mode Line
#+BEGIN_SRC emacs-lisp
;; Remove all minor modes (mode-line-modes)
(setq-default mode-line-format
'("%e"
mode-line-front-space
mode-line-mule-info
mode-line-client
mode-line-modified
mode-line-remote
mode-line-frame-identification
mode-line-buffer-identification
" "
mode-line-position
(vc-mode vc-mode)
" (" mode-name ") "
mode-line-misc-info
mode-line-end-spaces))

;; Add Date
(setq display-time-day-and-date t
display-time-format "%a %b %d %R"
display-time-interval 60
display-time-default-load-average nil)
(display-time)
#+END_SRC

** Theme
Emacs 28.1 now includes the [[https://www.gnu.org/software/emacs/manual/html_mono/modus-themes.html][Modus Themes]], which were carefully
designed to comply with the highest accessibility standard for color
contrast and supports nearly all of the popular third-party Emacs
packages as well.

However, I like to disable some of the programming related font-faces via =customize-face=.

#+BEGIN_SRC emacs-lisp
(use-package emacs
:init
(setq modus-themes-syntax '(faint yellow-comments))
(setq modus-themes-paren-match '(intense))
(setq modus-themes-org-blocks 'gray-background)
:custom-face
(eglot-highlight-symbol-face ((t :inherit underline)))
(font-lock-builtin-face ((t (:inherit modus-themes-bold :foreground "nil" :weight bold))))
(font-lock-constant-face ((t (:inherit modus-themes-bold :foreground "nil" :weight bold))))
(font-lock-function-name-face ((t (:foreground "nil"))))
(font-lock-keyword-face ((t (:inherit modus-themes-bold :foreground "nil" :weight bold))))
(font-lock-negation-char-face ((t (:foreground "nil"))))
(font-lock-type-face ((t (:foreground "nil"))))
(font-lock-variable-name-face ((t (:foreground "nil"))))
:config
(load-theme 'modus-operandi))
#+END_SRC

* Major Modes
** Org
#+BEGIN_QUOTE
Org mode is for keeping notes, maintaining TODO lists, planning
projects, and authoring documents with a fast and effective plain-text
system.
#+END_QUOTE

#+BEGIN_SRC emacs-lisp
(use-package org
:requires (ob-core org-agenda org-capture ox ox-md ox-html)
:mode ("\\.org\\'" . org-mode)
:commands (org-babel-do-load-languages org-demote-subtree org-promote-subtree)
:bind (:map org-mode-map
("" . org-demote-subtree)
("" . org-promote-subtree))
:config
(setq org-export-backends '(ascii html icalendar latex odt md))
(setq org-src-fontify-natively t)
(setq org-log-done 'time)
(setq org-html-doctype "html5")
(setq org-html-html5-fancy t)
(setq org-export-headline-levels 6)
(setq org-export-with-smart-quotes t)
(setq org-adapt-indentation nil)
(setq org-edit-src-content-indentation 0)

;; Custom TODO keywords
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)" "CANCELED(c@)")))
(setq org-todo-keyword-faces
'(("TODO" :foreground "red" :weight bold)
("NEXT :foreground "blue :weight bold)
("DONE :foreground "forest green :weight bold)
("CANCELED" :foreground "forest green" :weight bold)))

;; setup org-capture
;; `M-x org-capture' to add notes. `C-u M-x org-capture' to visit file
(setq org-capture-templates
`(("t" "Tasks" entry (file ,(concat org-directory "/todo.org"))
"* TODO %?\n %U\n %i\n %a")
("n" "Notes" entry (file ,(concat org-directory "/notes.org"))
"* %?\n %U\n %i\n")))

;; setup org-agenda
(setq org-agenda-files (list org-directory))
(setq org-agenda-window-setup 'current-window)

;; Set up babel source-block execution
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t)
(haskell . t)
(C . t)
(shell . t)))

;; Set up latex
(setq org-export-with-LaTeX-fragments t)
(setq org-preview-latex-default-process 'imagemagick)

;; local variable for keeping track of pdf-process options
(setq pdf-processp nil)

;; Prevent Weird LaTeX class issue
(unless (boundp 'org-latex-classes)
(setq org-latex-classes nil))
(add-to-list 'org-latex-classes
'("per-file-class"
"\\documentclass{article}
[NO-DEFAULT-PACKAGES]
[EXTRA]")))

;; Other config
(defun toggle-org-latex-pdf-process ()
"Change org-latex-pdf-process variable.

Toggle from using latexmk or pdflatex. LaTeX-Mk handles BibTeX,
but opens a new PDF every-time."
(interactive)
(if pdf-processp
;; LaTeX-Mk for BibTex
(progn
(setq pdf-processp nil)
(setq org-latex-pdf-process
'("latexmk -pdflatex='pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f' -gg -pdf -bibtex-cond -f %f"))
(message "org-latex-pdf-process: latexmk"))

;; Plain LaTeX export
(progn
(setq pdf-processp t)
(setq org-latex-pdf-process
'("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
(message "org-latex-pdf-process: xelatex"))))
#+END_SRC

Include syntax highlighting when exporting Org documents to HTML.
#+begin_src emacs-lisp
(use-package htmlize
:ensure t)
#+end_src

** C-Family
#+BEGIN_SRC emacs-lisp
;; Use One True Brace Style (K&R style indentation)
(setq c-default-style "k&r"
c-basic-offset 4)

;; Use C-Mode for CUDA
(add-to-list 'auto-mode-alist '("\\.cu\\'" . c-mode))
#+END_SRC

** Ruby
The standard ~ruby-mode~ is pretty good on its own. In fact, it is
written and maintained by "Matz", who is the creator of Ruby itself.

However, you can get more linting data by using ~rubocop~, and
~solargraph~ is the canonical language server.

#+begin_src text
gem install rubocop solargraph solargraph-rails
#+end_src

#+BEGIN_SRC emacs-lisp
(use-package ruby-mode
:ensure t
:config
(defun my/ruby-mode-hook()
(setq tab-width 2)
(setq ruby-align-to-stmt-keywords nil)
(setq ruby-insert-encoding-magic-comment nil)
(setq flymake-mode 1)
(setq eldoc-mode 1))
:hook ((ruby-mode . my/ruby-mode-hook)))
#+END_SRC

#+begin_src emacs-lisp
(use-package rubocop
:ensure t
:config
(add-hook 'ruby-mode-hook #'rubocop-mode))
#+end_src

#+begin_src emacs-lisp
(use-package rspec-mode
:ensure t
:config
(setq rspec-primary-source-dirs '("app" "lib" "packs"))

(defun my/rspec-compile()
(interactive)
(rspec-compile
(read-from-minibuffer
"Rspec: "
(format "%s:%d" buffer-file-name (line-number-at-pos))))))
#+end_src

** Web Mode
- =C-c C-f=: folds html tags
- =C-c C-n=: moves between the start / end tag
- =C-c C-w=: shows problematic white-space

#+BEGIN_SRC emacs-lisp
(use-package web-mode
:ensure t
:mode ("\\.html\\'" "\\.php\\'" "\\.vue\\'" "\\.eex\\'" "\\.tmpl\\'")
:custom-face
(web-mode-doctype-face ((t (:inherit font-lock-builtin-face))))
(web-mode-html-attr-name-face ((t :foreground "nil")))
(web-mode-html-attr-value-face ((t (:inherit font-lock-string-face))))
(web-mode-html-tag-face ((t :foreground "nil")))
(web-mode-symbol-face ((t (:weight bold))))
:config
(add-to-list 'web-mode-comment-formats '("javascript" . "//"))
(setq web-mode-markup-indent-offset 2)
(setq web-mode-css-indent-offset 2)
(setq web-mode-code-indent-offset 2)
(setq web-mode-style-padding 0)
(setq web-mode-script-padding 0)

(defun my/web-mode-hook()
;; Do not over-indent method chains
(add-to-list 'web-mode-indentation-params '("lineup-calls" . nil)))

:hook ((web-mode . my/web-mode-hook)))
#+END_SRC

** Emmet
#+BEGIN_SRC emacs-lisp
(use-package emmet-mode
:ensure t
:commands (emmet-expand-line emmet-expand)
:bind (:map emmet-mode-keymap
("C-j" . emmet-expand-line)
("" . emmet-expand))
:config
(setq emmet-indentation 2)
(defadvice emmet-preview-accept (after expand-and-fontify activate)
"Update the font-face after an emmet expantion."
(font-lock-flush))
:hook ((sgml-mode . emmet-mode)
(web-mode . emmet-mode)
(css-mode . emmet-mode)
(js-jsx-mode . emmet-mode)))
#+END_SRC

** CSS
#+BEGIN_SRC emacs-lisp
(use-package css-mode
:mode ("\\css\\'" "\\.scss\\'" "\\.sass\\'")
:config
(setq css-indent-offset 2))
#+END_SRC

** Javascript
#+begin_src emacs-lisp
(use-package js
:config
(setq js-indent-level 2))
#+end_src

#+begin_src emacs-lisp
(use-package prettier-js
:hook ((js-mode . prettier-js-mode)
(web-mode . prettier-js-mode)))
#+end_src

#+begin_src emacs-lisp
(use-package add-node-modules-path
:hook ((js-mode . add-node-modules-path)
(web-mode . add-node-modules-path)))
#+end_src

** Compilation Mode
#+BEGIN_SRC emacs-lisp
(add-hook 'compilation-mode-hook (lambda () (setq truncate-lines t)))
#+END_SRC

** Language Server (eglot)
#+BEGIN_QUOTE
Eglot works primarily with Emacs' built-in libraries and not with
third-party replacements for those facilities.

- definitions can be found via xref-find-definitions;
- on-the-fly diagnostics are given by flymake-mode;
- function signature hints are given by eldoc-mode;
- completion can be summoned with completion-at-point.
- projects are discovered via project.el's API;
#+END_QUOTE

#+BEGIN_SRC emacs-lisp
(use-package eglot
:ensure t)
#+END_SRC

** Go
#+begin_src text
go get -u golang.org/x/tools/gopls
#+end_src

#+BEGIN_SRC emacs-lisp
(use-package go-mode
:ensure t
:config
(defun my/go-mode-hook()
(setq tab-width 2)
(setq gofmt-args '("-s"))
(add-hook 'before-save-hook 'gofmt-before-save)
(setq-local compile-command "go test")
(eglot-ensure))
:hook ((go-mode . my/go-mode-hook)))
#+END_SRC

** Magit
Magit is an amazing interface for using =git= within Emacs.

One of my favorite features is being able to quickly cycle through the
history of the file I'm looking at. To do this, execute
=magit-file-dispatch= (=C-c M-g=) and then use =n= and =p= to load the
file history into a read-only buffer.

#+Begin_SRC emacs-lisp
(use-package magit
:ensure t
:commands (magit-section-toggle)
:bind (:map magit-mode-map
("" . magit-section-toggle)))
#+END_SRC

** Ediff
Emacs diff tool. Can be activated from Magit by pressing =e= on a conflicting file.
Use =n, p= to jump between conflicts and select changes to keep using =a, b=.
#+BEGIN_SRC emacs-lisp
(use-package ediff
:config
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain))
#+END_SRC

** Bash
#+BEGIN_SRC emacs-lisp
(use-package flymake-shellcheck
:ensure t
:init
(add-hook 'sh-mode-hook 'flymake-shellcheck-load))
#+END_SRC

** Dired
#+BEGIN_SRC emacs-lisp
(use-package dired
:config
(setq dired-dwim-target t)
(setq dired-listing-switches "-Alpvh") ; ls flags
:hook ((dired-after-readin . hl-line-mode)))
#+END_SRC

* Minor Modes
** Wgrep
Like =wdired= for =rgrep= and =ag-project=.

- =C-c C-p= to enable (=wgrep-change-to-wgrep-mode=)
- =C-c C-c= to execute
- =C-c C-k= to abort

#+BEGIN_SRC emacs-lisp
(use-package wgrep
:ensure t
:config
(setq wgrep-auto-save-buffer t))
#+END_SRC

** Ripgrep
=rg.el= provides a nice interface on top of =rgrep= and includes support for =wgrep=.

Results Buffer:
- =m= Open menu
- =e= Toggle wgrep
- =t= Interpret search string literally
- =r= Interpret search string as regexp
- =c= Toggle case sensitivity
- =g= Rerun search

#+begin_src emacs-lisp
(use-package rg
:ensure t
:config
(rg-define-search my/rg-project
"Search for any files in project or current directory"
:query ask
:format literal
:confirm prefix
:files "everything"
:flags ("--hidden -g !.git")
:dir (if (vc-root-dir)
(vc-root-dir)
default-directory))
:bind
("M-SPC" . my/rg-project))
#+end_src

** Rainbow Mode
=rainbow-mode= highlights color codes in a given buffer.
#+BEGIN_SRC emacs-lisp
(use-package rainbow-mode
:ensure t
:hook ((web-mode . rainbow-mode)
(css-mode . rainbow-mode)))
#+END_SRC

** Flyspell
Enable spell-checking in Emacs using [[http://aspell.net/][Aspell]]

#+BEGIN_SRC emacs-lisp
(use-package flyspell
:ensure t
:config
(setq flyspell-issue-welcome-flag nil)
(setq flyspell-issue-message-flag nil)
(setq flyspell-mark-duplications-flag nil)
(setq-default ispell-program-name "aspell")
(setq-default ispell-list-command "list")
(define-key flyspell-mouse-map [down-mouse-3] 'flyspell-correct-word)
(define-key flyspell-mouse-map [mouse-3] 'undefined)
:hook ((text-mode . flyspell-mode)
(org-mode . flyspell-mode)
(prog-mode . flyspell-prog-mode)))
#+END_SRC

*** Helpful Default Keybindings
=C-.= corrects word at point.
=C-,​= to jump to next misspelled word.

** Expand Region
[[https://github.com/magnars/expand-region.el][Expand-region]] can make selections based on semantic units / delimiters
like quotes, parens, or markup tags.

#+BEGIN_SRC emacs-lisp
(use-package expand-region
:ensure t
:commands (er/expand-region)
:bind ("C-=" . er/expand-region))
#+END_SRC

** Skeleton Mode
[[http://www.emacswiki.org/emacs/SkeletonMode][Skeleton mode]] provides a way to define =elisp= functions that evaluate
into dynamic / static templates.

#+BEGIN_SRC emacs-lisp
;; Global
(defun insert-date (str)
"Insert current date in ISO 8601.
Typing 'v' will insert the current date verbosely.
Typing 't' will append the time in H:M:S to either format."
(interactive "sType (v) for verbose date | (t) for time: ")
(if (string-match-p "v" str)
(insert (format-time-string "%B %e, %Y"))
(insert (format-time-string "%Y-%m-%d")))
(when (string-match-p "t" str)
(insert (format-time-string " %T"))))

(define-skeleton insert-iso-date-skeleton
"Skeleton wrapper for INSERT-DATE"
"ISO Date"
'(insert-date ""))

(define-skeleton insert-verbose-date-skeleton
"Skeleton wrapper for INSERT-DATE"
"Verbose Date"
'(insert-date "v"))

;; Org
(define-skeleton org-skeleton-header
"Insert document headers."
"Title: "
"#+TITLE: " str | (buffer-name) "\n"
"#+AUTHOR: " (user-full-name) "\n"
"#+DATE: " (insert-date "v") "\n"
"#+OPTIONS: ':true *:true toc:nil num:nil ^:nil\n" _)

(define-skeleton org-skeleton-latex-header
"Insert document headers and essential LaTeX header options."
"options"
'(org-skeleton-header)
"\n#+LaTeX_HEADER: \\renewcommand{\\thesection}{\\hspace*{-1.0em}}\n"
"#+LaTeX_HEADER: \\renewcommand{\\thesubsection}{\\hspace*{-1.0em}}\n"
"#+LaTeX_HEADER: \\setlength{\\parindent}{0pt}\n"
"#+LaTeX_HEADER: \\usepackage[margin=1in]{geometry}\n" _)

;; LaTeX
(define-skeleton latex-skeleton-begin
"Insert a LaTeX BEGIN block."
"Block type: "
"\\begin{" str | "align*" "}\n" _ "\n\\end{" str | "align*" "}\n")

;; BibTeX
(defun bibtex-insert-citation (str)
"Insert a BibTeX citation.
Begin by inserting the citation type, then call
BIBTEX-SKELETON-CITATION to prompt for a label and insert the rest."
(interactive "s(a)rticle | (b)ook | (c)ollection | (w)ebsite: ")
(let ((type))
(cond ((string-match-p "^a\\|rticle" str)
(setq type "article"))
((string-match-p "^b\\|ook" str)
(setq type "book"))
((string-match-p "^c\\|ollection" str)
(setq type "incollection"))
((string-match-p "^w\\|ebsite" str)
(setq type "misc")))
(insert "@"type"{"))
(bibtex-skeleton-citation))

(define-skeleton bibtex-skeleton-citation
"Insert the contents of a BibTeX citation starting with the label."
"Label: "
str | "label" ",\n"
>"author = \"\",\n"
>"title = \"\",\n"
>"%journal = \"\",\n"
>"%booktitle = \"\",\n"
>"%publisher = \"\",\n"
>"%editor = \"\",\n"
>"%volume = \"\",\n"
>"%number = \"\",\n"
>"%series = \"\",\n"
>"%edition = \"\",\n"
>"%address = \"\",\n"
>"%type = \"\",\n"
>"%chapter = \"\",\n"
>"%pages = \"\",\n"
>"%year = \"\",\n"
>"%month = \"\",\n"
>"%url = \"\",\n"
>"note = \"Accessed " '(insert-date "t") "\",\n"
"},\n" _
)

(define-skeleton bibtex-skeleton-insert-citation
"Skeleton wrapper for BIBTEX-INSERT-CITATION"
"(a)rticle | (b)ook | (c)ollection | (w)ebsite: "
"(bibtex-insert-citation \"" str "\")"_)

;; C
(define-skeleton c-skeleton-hello
"Inserts a simple 'hello-world' program in C."
nil
"#include\n\n"
"int main (int argc, char *argv[]) {\n"
_ >"printf(\"%s\", \"Hello world.\\n\");\n"
>"return 0;\n"
"}\n")

;; Go
(define-skeleton go-err-check
"Go error check boilerplate"
nil
"if err != nil {\n"
> _"\n"
"}\n")

(define-skeleton go-append
"go append() boilerplate"
nil
'(setq v1 (skeleton-read "var? "))
> v1 " = append(" v1 ", " _ ")")

(define-skeleton go-test-case
"go t.Run boilerplate"
nil
"t.Run(\""_"\", func(t *testing.T) {\n"
> "\n"
"})\n")

;; JavaScript
(define-skeleton js-skeleton-jest
"Inserts a test block for jest."
nil
_"('', () => {\n"
>"\n"
"});\n")

(define-skeleton js-skeleton-log
"Inserts console.log()"
nil
"console.log("_")"\n)

;; Ruby
(define-skeleton ruby-skeleton-rspec
"Inserts a test block for rspec"
nil
"it \""_"\" do\n\n"
"end\n")
#+END_SRC

** Abbrev Mode
[[http://www.emacswiki.org/emacs/AbbrevMode#toc6][Abbrev mode]] is a built-in tool that expands abbreviations (or evaluates =elisp=).
Combining an =abbrev= expansion with a =skeleton= template is very powerful.
Expansions can be either global or local to a specific major mode.

#+BEGIN_SRC emacs-lisp
;; enable abbrev for all buffers
(use-package abbrev
:init
(setq-default abbrev-mode t))

;; Abbrev Tables
(define-abbrev-table 'global-abbrev-table
'(
("8date" "" insert-iso-date-skeleton 0)
("8today" "" insert-verbose-date-skeleton 0)
))

(define-abbrev-table 'org-mode-abbrev-table
'(
("8header" "" org-skeleton-header 0)
("8lheader" "" org-skeleton-latex-header 0)
("8begin" "" latex-skeleton-begin 0)
))

(define-abbrev-table 'bibtex-mode-abbrev-table
'(
("8cite" "" bibtex-skeleton-insert-citation 0)
))

(define-abbrev-table 'c-mode-abbrev-table
'(
("8hello" "" c-skeleton-hello 0)
))

(define-abbrev-table 'go-mode-abbrev-table
'(
("8err" "" go-err-check 0)
("8append" "" go-append 0)
("8test" "" go-test-case 0)
))

(define-abbrev-table 'js-mode-abbrev-table
'(
("8jest" "" js-skeleton-jest 0)
("8log" "" js-skeleton-log 0)
))

(define-abbrev-table 'web-mode-abbrev-table
'(
("8log" "" js-skeleton-log 0)
))

(define-abbrev-table 'ruby-mode-abbrev-table
'(
("8it" "" ruby-skeleton-rspec)
))

;; stop asking whether to save newly added abbrev when quitting emacs
(setq save-abbrevs nil)
#+END_SRC

** Git Link
[[https://github.com/sshaw/git-link][git-link]] will open your web browser to a specific line or region of a file under source control.
#+BEGIN_SRC emacs-lisp
(use-package git-link
:ensure t
:config
(setq git-link-open-in-browser t))
#+END_SRC

* Custom Functions
** Reload current file without confirmation
#+begin_src emacs-lisp
(defun my/reload-buffer ()
(interactive)
(revert-buffer t t))
#+end_src

** Create new scratch buffer
#+BEGIN_SRC emacs-lisp
(defun my/create-scratch-buffer nil
"Create a new scratch buffer to work in."
(interactive)
(let ((n 0)
(bufname nil))
(while (progn
(setq bufname (if (= n 0) "*scratch*" (format "*scratch%d" n)))
(setq n (1+ n))
(get-buffer bufname)))
(switch-to-buffer (get-buffer-create bufname))
(org-mode)))
#+END_SRC

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

** Delete Buffer & File
#+BEGIN_SRC emacs-lisp
(defun my/delete-current-buffer-file ()
"Removes file connected to current buffer and kills buffer."
(interactive)
(let ((filename (buffer-file-name))
(buffer (current-buffer))
(name (buffer-name)))
(if (not (and filename (file-exists-p filename)))
(kill-buffer buffer)
(when (yes-or-no-p "Are you sure you want to remove this file? ")
(delete-file filename)
(kill-buffer buffer)
(message "File '%s' successfully removed" filename)))))
#+END_SRC

** Remove Secondary Selection
#+BEGIN_SRC emacs-lisp
(defun my/unset-secondary-selection ()
(interactive)
(delete-overlay mouse-secondary-overlay))
#+END_SRC

** ANSI Color Codes
#+BEGIN_SRC emacs-lisp
(use-package ansi-color
:config
(defun my/ansi-color (&optional beg end)
"Interpret ANSI color esacape sequence by colorifying content. Operate on selected region or whole buffer."
(interactive
(if (use-region-p)
(list (region-beginning) (region-end))
(list (point-min) (point-max))))
(let ((inhibit-read-only t))
(ansi-color-apply-on-region beg end))))
#+END_SRC

* Miscellaneous
** macOS
#+BEGIN_SRC emacs-lisp
;; Are we on a Mac?
(when (equal system-type 'darwin)
(use-package exec-path-from-shell
:ensure t
:init
(menu-bar-mode 1)

;; Set modifier keys
(setq mac-command-modifier 'super)
(setq mac-option-modifier 'meta)
(setq mac-control-modifier 'control)
(setq ns-function-modifier 'hyper)

;; Use right option for spacial characters.
(setq mac-right-option-modifier 'none)

(exec-path-from-shell-initialize)
(setq-default ispell-program-name "/usr/local/bin/aspell")))
#+END_SRC

* Server / Client
#+BEGIN_SRC emacs-lisp
(server-start)
#+END_SRC