Skip to content

agoodno/dotemacs

Repository files navigation

“Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish.” -Neal Stephenson, “In the Beginning was the Command Line”

See GNU Emacs Lisp Reference Manual

Emacs Config

Package initialization

(require 'package)
(setq package-enable-at-startup nil)

;; Used to fix issue with package install of org mode
;; (setq package-check-signature nil)
;; (setq package-check-signature "allow-unsigned")
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

(when (not package-archive-contents)
  (package-refresh-contents))

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

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

(setq load-prefer-newer t)

Custom initialization

;; set custom file location
(setq custom-file (concat user-emacs-directory "custom.el"))
(load custom-file :noerror)

General config

User variables

Define custom user variables that get set once per machine. Bootstrap by customizing the agg group and use Apply and Save to save the values to the custom.el for future sessions.

customize-group <RET>
agg
(defcustom user-full-name ""
  "Name used in tool config."
  :type 'string
  :group 'agg)

(defcustom user-email-address ""
  "Email used in tool config."
  :type 'string
  :group 'agg)

(defcustom notes-location "/path/to/notes/repo"
  "Location of the checked out notes git repo."
  :type 'string
  :group 'agg)

;; Consider setting these variables in future
;; system-name ""
;; user-login-name ""

Editor

(use-package emacs
  :custom
  ;; TAB cycle if there are only few candidates
  ;; (completion-cycle-threshold 3)

  ;; Enable indentation+completion using the TAB key.
  ;; `completion-at-point' is often bound to M-TAB.
  (tab-always-indent 'complete)

  ;; Emacs 30 and newer: Disable Ispell completion function.
  ;; Try `cape-dict' as an alternative.
  (text-mode-ispell-word-completion nil)

  ;; Hide commands in M-x which do not apply to the current mode.  Corfu
  ;; commands are hidden, since they are not used via M-x. This setting is
  ;; useful beyond Corfu.
  (read-extended-command-predicate #'command-completion-default-include-p))

  ;; Note: *inhibit-startup-message* and *inhibit-splash-screen* are aliases for this variable
  ;; Turn off startup splash
  (setq inhibit-startup-screen t)

  ;; Turn off menu
  (menu-bar-mode -1)

  ;; Turn off tool-bar
  (when (fboundp 'tool-bar-mode)
    (tool-bar-mode -1))

  ;; Turn off scroll-bar
  (when (fboundp 'scroll-bar-mode)
    (scroll-bar-mode -1))

  ;; Turn off initial scratch buffer text message
  (setq initial-scratch-message nil)

  ;; Disable line wrap
  (setq default-truncate-lines t)

  ;; Highlight selected regions
  (setq transient-mark-mode t)

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

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

  ;; Change backups to go to a backup directory
  (setq backup-directory-alist '(("." . "~/.emacs.d/backups")))

  ;; Auto revert all files changed elsewhere
  (global-auto-revert-mode t)

  ;; Set pager to cat for shell
  (setenv "PAGER" "/bin/cat")

  ;; Compilation buffer

  ;; Scroll with output automatically
  (setq compilation-scroll-output t)
  ;;(setq compilation-scroll-output 'first-error)

  ;; Show on top of current buffer instead of new buffer
  (add-to-list 'same-window-buffer-names "*compilation*")

Environment

(use-package exec-path-from-shell
  :ensure t
  :init
  ;;(setq shell-file-name "/usr/local/bin/zsh")
  (setq exec-path-from-shell-variables '("PATH" "MANPATH")) ;; "PKG_CONFIG_PATH" "LDFLAGS"
  :config
  (exec-path-from-shell-initialize)
  ;; Make ansi-term play nice with zsh prompt.
  (defadvice ansi-term (after advise-ansi-term-coding-system)
    (set-buffer-process-coding-system 'utf-8-unix 'utf-8-unix)))

Look & Feel

Bell

(defun agg/friendly-visible-bell ()
  "A friendlier visual bell effect."
  (invert-face 'mode-line)
  (run-with-timer 0.3 nil 'invert-face 'mode-line))

(setq visible-bell nil
      ring-bell-function #'agg/friendly-visible-bell)

Color

(use-package ansi-color
  :ensure t
  :init
  (defun agg/ansi-colorize-buffer ()
    (let ((buffer-read-only nil))
      (ansi-color-apply-on-region (point-min) (point-max))))
  (add-hook 'compilation-filter-hook 'agg/ansi-colorize-buffer))

Themes

(use-package color-theme-sanityinc-tomorrow
  :ensure t)

(use-package dracula-theme
  :ensure t)

(use-package gruvbox-theme
  :ensure t)

(use-package catppuccin-theme
  :ensure t
  :init
  ;; from lightest to darkest
  (setq catppuccin-flavor 'latte)
  ;; (setq catppuccin-flavor 'frappe)
  ;; (setq catppuccin-flavor 'macchiato)
  ;; (setq catppuccin-flavor 'mocha)
  )

(use-package spacemacs-theme
  :ensure t
  :init
  (setq spacemacs-theme-org-agenda-height nil)
  (setq spacemacs-theme-org-height nil))

(use-package timu-spacegrey-theme
  :ensure t
  :init
  (customize-set-variable 'timu-spacegrey-flavour "light")
  (customize-set-variable 'timu-spacegrey-contrasted-comments nil)
  (customize-set-variable 'timu-spacegrey-scale-org-document-title t)
  (customize-set-variable 'timu-spacegrey-scale-org-document-info t)
  (customize-set-variable 'timu-spacegrey-scale-org-level-1 t)
  (customize-set-variable 'timu-spacegrey-scale-org-level-2 t)
  (customize-set-variable 'timu-spacegrey-scale-org-level-3 t))

;; Use M-x customize-themes to save theme choice to custom.el so it saves across sessions without manually loading here
;;
;; (load-theme 'agg-light t)
;; (load-theme 'agg-dark t)

;; (setq catppuccin-flavor 'frappe) ;; or 'latte, 'macchiato, or 'mocha
;; (catppuccin-reload)
;;
;; (load-theme 'catppuccin :no-confirm)

;; (load-theme 'sanityinc-tomorrow-day)
;; (load-theme 'sanityinc-tomorrow-night)
;; (load-theme 'sanityinc-tomorrow-blue)
;; (load-theme 'sanityinc-tomorrow-bright)
;; (load-theme 'sanityinc-tomorrow-eighties)

;; (load-theme 'dracula t)

;; (load-theme 'gruvbox t) ;; sets to default
;; (load-theme 'gruvbox-light-soft t)
;; (load-theme 'gruvbox-light-medium t)
;; (load-theme 'gruvbox-light-hard t)
;; (load-theme 'gruvbox-dark-soft t)
;; (load-theme 'gruvbox-dark-medium t) ;; default

;; (load-theme 'spacemacs-light t)
;; (load-theme 'spacemacs-dark t)

;; (load-theme 'timu-spacegrey t)

Indenting

(show-paren-mode 1)
(setq-default indent-tabs-mode nil)

;; Indent with spaces, never with TABs
(setq-default indent-tabs-mode nil)

;; Sets basic offset
(setq c-basic-offset 2)

;; Indent to 2 spaces
(setq-default tab-width 2)

;; Tabs stop every 2 spaces
(setq-default tab-stop-list (quote (2 4 6 8 10)))

(use-package aggressive-indent
  :ensure t)

Line and cursor

(global-hl-line-mode +1)

;; (use-package bar-cursor
;;   :ensure t
;;   :init (bar-cursor-mode 1))

(use-package beacon
  :ensure t
  :init (beacon-mode 1))

Selection

(setq x-select-enable-clipboard t
      ;; x-select-enable-primary t ;;causes problems with delete-selection-mode
      save-interprogram-paste-before-kill nil
      apropos-do-all t
      mouse-yank-at-point nil)

;; Paste and backspace operations delete the selection and "pastes over" it
(delete-selection-mode t)

Windowing

;; Make side by side buffers function the same as the main window
(setq truncate-partial-width-windows nil)

(setq split-width-threshold nil)

Sidebar

(use-package dired-sidebar
  :ensure t
  :bind (("C-x C-n" . dired-sidebar-toggle-sidebar))
  :commands (dired-sidebar-toggle-sidebar)
  :init
  (add-hook 'dired-sidebar-mode-hook
            (lambda ()
              (unless (file-remote-p default-directory)
                (auto-revert-mode))))
  :config
  (push 'toggle-window-split dired-sidebar-toggle-hidden-commands)
  (push 'rotate-windows dired-sidebar-toggle-hidden-commands)

  (setq dired-sidebar-subtree-line-prefix "__")
  ;; (setq dired-sidebar-theme 'vscode)
  (setq dired-sidebar-use-term-integration t)
  (setq dired-sidebar-use-custom-font t))

Modeline

(use-package spaceline
  :ensure t
  :init
  (setq powerline-default-separator 'arrow-fade)
  :config
  (spaceline-spacemacs-theme))

Functions

(defun agg/untabify-buffer ()
  "Untabify current buffer"
  (interactive)
  (untabify (point-min) (point-max)))

(defun agg/progmodes-before-save-hook ()
  "Hooks which run on file write for programming modes"
  (require 'whitespace)

  (prog1 nil
    (set-buffer-file-coding-system 'utf-8-unix)
    (agg/untabify-buffer)
    (whitespace-cleanup)))

(defun agg/progmodes-hooks ()
  "Hooks for programming modes"
  (add-hook 'before-save-hook 'agg/progmodes-before-save-hook))

(defun agg/shell-dir (name dir)
  "Opens a shell into the specified directory
         ex. (shell-dir "cmd-rails" "/Users/agoodnough/src/rails/")"
  (let ((default-directory dir))
    (shell name)))

(defun agg/insert-current-date ()
  (interactive)
  (insert (shell-command-to-string "echo -n $(date %Y-%m-%d)")))

(require 'calendar)
(defun insdate-insert-current-date (&optional omit-day-of-week-p)
  "Insert today's date using the current locale.
          With a prefix argument, the date is inserted without the day of
          the week."
  (interactive "P*")
  (insert (calendar-date-string (calendar-current-date) nil
                                omit-day-of-week-p)))

(defun agg/insert-date (prefix)
  "Insert the current date. With prefix-argument, use ISO format. With
           two prefix arguments, write out the day and month name."
  (interactive "P")
  (let ((format "%Y-%m-%d")
        (system-time-locale "en_US"))
    (insert (format-time-string format))))

(defun agg/ins-tommorrows-date ()
  (interactive)
  (insert (format-time-string "%A, %B %e, %Y" (time-add (current-time) (seconds-to-time (* 60 (* 60 (* 24))))))))

;; (float-time)
;; (calendar-date-string (decode-time (seconds-to-time (+ (* 60 (* 60 (* 24))) (float-time (current-time))))))

;; (format-time-string "%A, %B %e, %Y" (decode-time (time-add (current-time) (seconds-to-time (* 60 (* 60 (* 24)))))))

;; (seconds-to-time (* 60 (* 60 (* 24))))

;; (format-time-string "%A, %B %e, %Y" (current-time))
;; (format-time-string "%A, %B %e, %Y" (time-add (current-time) (seconds-to-time (* 60 (* 60 (* 24))))))
;; (decode-time (seconds-to-time (+ (float-time (current-time)) (* 60 (* 60 (* 24))))))

(defun agg/back-window ()
  (interactive)
  (other-window -1))

(defun agg/log-region (&optional arg)
  "Keyboard macro."
  (interactive "p")
  (kmacro-exec-ring-item
   (quote ([134217847 16 5 return 112 117 116 115 32 34 25 61 35 123 25 125 34] 0 "%d")) arg))

(defun agg/turn-off-mouse (&optional frame)
  (interactive)
  (shell-command "xinput --disable \"SynPS/2 Synaptics TouchPad\""))

(defun agg/turn-on-mouse (&optional frame)
  (interactive)
  (shell-command "xinput --enable \"SynPS/2 Synaptics TouchPad\""))

(defun agg/register-mouse-hooks ()
  (progn
    (add-hook 'focus-in-hook #'agg/turn-off-mouse)
    (add-hook 'focus-out-hook #'agg/turn-on-mouse)
    (add-hook 'delete-frame-functions #'agg/turn-on-mouse)
    (add-hook 'kill-emacs-hook #'agg/turn-on-mouse)))

;; new style for 27.1 (doesn't quite work yet)
;; (defun register-new-mouse-hooks ()
;;   (progn
;;    (add-function :after after-focus-change-function #'agg/turn-off-mouse)
;;    (add-function :after after-focus-change-function #'agg/turn-on-mouse)
;;    (add-function :after delete-frame-functions #'agg/turn-on-mouse)))

(if (string-equal system-type "gnu/linux")
    (agg/register-mouse-hooks))

Key Bindings

;; Align your code in a pretty way.
(global-set-key (kbd "C-x \\") 'align-regexp)

;; Completion that uses many different methods to find options.
(global-set-key (kbd "M-/") 'hippie-expand)

;; Use regex searches by default.
(global-set-key (kbd "C-s") 'isearch-forward-regexp)
(global-set-key (kbd "C-r") 'isearch-backward-regexp)
(global-set-key (kbd "C-M-s") 'isearch-forward)
(global-set-key (kbd "C-M-r") 'isearch-backward)

;; Buffers
(global-set-key (kbd "C-c y") 'bury-buffer)
(global-set-key (kbd "M-`") 'file-cache-minibuffer-complete)
                                        ; Use ibuffer which is better than switch buffer
(global-set-key (kbd "C-x C-b") 'ibuffer)

;; Insert
(global-set-key "\C-x\M-d" `insdate-insert-current-date)

;; Window switching. (C-x o goes to the next window)
(windmove-default-keybindings) ;; Shift+direction
(global-set-key (kbd "C-x O") (lambda () (interactive) (other-window -1))) ;; back one
(global-set-key (kbd "C-x C-o") (lambda () (interactive) (other-window 2))) ;; forward two

;; Start eshell or switch to it if it's active.
(global-set-key (kbd "C-x m") 'eshell)

;; Start a new eshell even if one is active.
(global-set-key (kbd "C-x M") (lambda () (interactive) (eshell t)))

;; Start a regular shell if you prefer that.
(global-set-key (kbd "C-x M-m") 'shell)

;; If you want to be able to M-x without meta (phones, etc)
(global-set-key (kbd "C-x C-m") 'execute-extended-command)

;; Fetch the contents at a URL, display it raw.
(global-set-key (kbd "C-x C-h") 'view-url)

;; Help should search more than just commands
(global-set-key (kbd "C-h a") 'apropos)

;; Should be able to eval-and-replace anywhere.
(global-set-key (kbd "C-c e") 'eval-and-replace)

;; For debugging Emacs modes
(global-set-key (kbd "C-c p") 'message-point)

;; Comment or uncomment region
(global-set-key (kbd "C-c C-;") 'comment-or-uncomment-region)

;; Activate occur easily inside isearch
(define-key isearch-mode-map (kbd "C-o")
            (lambda () (interactive)
              (let ((case-fold-search isearch-case-fold-search))
                (occur (if isearch-regexp isearch-string (regexp-quote isearch-string))))))

(define-key global-map (kbd "C-M-+") 'text-scale-increase)
(define-key global-map (kbd "C-M-_") 'text-scale-decrease)

;(global-set-key "\C-q" 'backward-kill-word)

;;Permanently, force TAB to insert just one TAB (in every mode):
;; (global-set-key (kbd "TAB") 'tab-to-tab-stop)

;;Opens browser to url
(global-set-key (kbd "C-x C-u") 'browse-url)
(global-set-key (kbd "C-c C-o") 'browse-url)

;;Toggles whitespace
(global-set-key (kbd "C-c w") 'whitespace-mode)

;; Launch a new shell. Use "C-u" to be prompted for the shell's name
(global-set-key [f2] 'shell)

;; Refresh file from disk
(global-set-key [f5] 'revert-buffer)

;; Moves current buffer to last buffer
(global-set-key [f6] 'bury-buffer)

;; Moves last buffer to current buffer
(global-set-key [f7] 'unbury-buffer)

;; In shell, moves the prompt to the line of previously executed command
(global-set-key [f8] 'comint-previous-prompt)

(global-set-key [f9] 'undo)

(global-set-key [f11] 'whitespace-mode)

;; Unset F10 for tmux chicanery
;; https://superuser.com/questions/1142577/bind-caps-lock-key-to-tmux-prefix-on-macos-sierra
(global-unset-key [f10])

(global-set-key [f12] 'toggle-truncate-lines)

(global-set-key (kbd "C-,") 'agg/back-window)

(global-set-key (kbd "C-.") 'other-window)

(global-set-key (kbd "s-p") 'previous-buffer)

(global-set-key (kbd "s-n") 'next-buffer)

(global-set-key (kbd "C-x C-l") 'agg/log-region)

;; Two approaches are discussed here for local key bindings
;; http://stackoverflow.com/questions/9818307/emacs-mode-specific-custom-key-bindings-local-set-key-vs-define-key

;; This is a general approach to binding a specific key binding to one
;; or more modes. Should be used in this file.
;; (defun agg/bindkey-recompile ()
;;   "Bind <F5> to `recompile'."
;;   (local-set-key (kbd "<f5>") 'recompile))
;; (add-hook 'c-mode-common-hook 'agg/bindkey-recompile)

Features

Completions

(use-package vertico
  :ensure t
  :init
  (vertico-mode t))

;; Optionally use the `orderless' completion style.
(use-package orderless
  :ensure t
  :custom
  ;; Configure a custom style dispatcher (see the Consult wiki)
  ;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch))
  ;; (orderless-component-separator #'orderless-escapable-split-on-space)
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles partial-completion)))))

(use-package marginalia
  :ensure t
  :init
  (marginalia-mode t))

(use-package consult
  :ensure t
  :init
  (setq register-preview-delay 0.5
        register-preview-function #'consult-register-format)
  :bind (("C-c M-x" . consult-mode-command)
         ("C-c h" . consult-history)
         ("C-c k" . consult-kmacro)
         ("C-c m" . consult-man)
         ("C-c i" . consult-info)
         ([remap Info-search] . consult-info)
         ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
         ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
         ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
         ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
         ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
         ("C-x r b" . consult-bookmark)            ;; orig. bookmark-jump
         ("C-x p b" . consult-project-buffer)      ;; orig. project-switch-to-buffer
         ;; Custom M-# bindings for fast register access
         ("M-#" . consult-register-load)
         ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
         ("C-M-#" . consult-register)
         ;; Other custom bindings
         ("M-y" . consult-yank-pop)                ;; orig. yank-pop
         ;; M-g bindings in `goto-map'
         ("M-g e" . consult-compile-error)
         ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
         ("M-g g" . consult-goto-line)             ;; orig. goto-line
         ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
         ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
         ("M-g m" . consult-mark)
         ("M-g k" . consult-global-mark)
         ("M-g i" . consult-imenu)
         ("M-g I" . consult-imenu-multi)
         ;; M-s bindings in `search-map'
         ("M-s d" . consult-find)                  ;; Alternative: consult-fd
         ("M-s c" . consult-locate)
         ("M-s g" . consult-grep)
         ("M-s G" . consult-git-grep)
         ("M-s r" . consult-ripgrep)
         ("M-s l" . consult-line)
         ("M-s L" . consult-line-multi)
         ("M-s k" . consult-keep-lines)
         ("M-s u" . consult-focus-lines)
         ;; Isearch integration
         ("M-s e" . consult-isearch-history)
         :map isearch-mode-map
         ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
         ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
         ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
         ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch
         ;; Minibuffer history
         :map minibuffer-local-map
         ("M-s" . consult-history)                 ;; orig. next-matching-history-element
         ("M-r" . consult-history)))

(use-package corfu
  :ensure t
  ;; Optional customizations
  ;; :custom
  ;; (corfu-cycle t)                ;; Enable cycling for `corfu-next/previous'
  ;; (corfu-quit-at-boundary nil)   ;; Never quit at completion boundary
  ;; (corfu-quit-no-match nil)      ;; Never quit, even if there is no match
  ;; (corfu-preview-current nil)    ;; Disable current candidate preview
  ;; (corfu-preselect 'prompt)      ;; Preselect the prompt
  ;; (corfu-on-exact-match nil)     ;; Configure handling of exact matches

  ;; Enable Corfu only for certain modes. See also `global-corfu-modes'.
  ;; :hook ((prog-mode . corfu-mode)
  ;;        (shell-mode . corfu-mode)
  ;;        (eshell-mode . corfu-mode))

  ;; Recommended: Enable Corfu globally.  This is recommended since Dabbrev can
  ;; be used globally (M-/).  See also the customization variable
  ;; `global-corfu-modes' to exclude certain modes.
  :init
  (global-corfu-mode))

Organization

Configuration for Org Mode.

(use-package org
  :init
  (setq org-log-done 'time)
  (setq org-log-done 'note)
  (setq org-todo-keywords
        '((sequence "TODO" "INPROGRESS" "|" "DONE" "CANCELED")))
  (setq org-log-done nil)
  (setq org-hide-leading-stars t)
  (add-hook 'org-mode-hook (lambda () (org-bullets-mode t)))
  :bind (("C-c h" . org-store-link) ;; h for href
         ("C-c c" . org-capture)
         ("C-c a" . org-agenda)
         ("C-c t" . ins-tommorrows-date)
         ("C-c d" . insdate-insert-current-date)
         :map org-mode-map
         ("C-c !" . org-time-stamp-inactive))
  :mode ("\\.org$" . org-mode)
  :config
  (require 'org-id))

(use-package org-bullets
  :ensure t)

;; (require 'ob-sh)
;; (org-babel-do-load-languages 'org-babel-load-languages '((shell . t)))

Notes

Configuration for Org Roam.

(use-package org-roam
  :ensure t
  :after org
  :init
  (setq org-roam-v2-ack t) ;; acknowledge upgrade and remove warning at startup
  :custom
  (org-roam-directory notes-location)
  (org-roam-completion-everywhere t)
  (org-roam-db-update-on-save t)
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n r" . org-roam-node-random)
         :map org-mode-map
         ("C-M-i" . completion-at-point)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n o" . org-id-get-create)
         ("C-c n t" . org-roam-tag-add)
         ("C-c n a" . org-roam-alias-add)
         ("C-c n l" . org-roam-buffer-toggle)
         :map org-roam-dailies-map
         ("Y" . org-roam-dailies-capture-yesterday)
         ("T" . org-roam-dailies-capture-tomorrow))
  :bind-keymap
  ("C-c n d" . org-roam-dailies-map)
  :config
  (require 'org-roam-dailies) ;; Ensure the keymap is available
  (defun my-set-fill-column () (setq-local fill-column 180))
  (add-hook 'org-roam-find-file-hook #'my-set-fill-column)
  (add-hook 'org-roam-find-file-hook #'turn-on-auto-fill)
  (org-roam-db-autosync-mode))

;; Use M-x org-roam-ui-mode RET to enable the global mode. It will start a web server on http://127.0.0.1:35901/ and connect to it via a WebSocket for real-time updates.
(use-package org-roam-ui
  :ensure t
  :after org-roam
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t))

Browsing

;; (use-package w3m
;;   :ensure t)

Killing/Yanking

(use-package browse-kill-ring
  :ensure t)

Development

General

(use-package smartparens
  :ensure t
  :init
  (require 'smartparens-config))

(use-package yasnippet
  :ensure t
  :init
  (yas-global-mode 1))

(use-package lsp-mode
  :ensure t
  :commands (lsp lsp-deferred))

;; optionally
(use-package lsp-ui
  :ensure t
  :commands lsp-ui-mode
  :after lsp)

;; optionally if you want to use debugger
;; (use-package dap-mode)
;; (use-package dap-LANGUAGE) to load the dap adapter for your language

(use-package rg
  :ensure t
  :config
  (rg-enable-default-bindings))

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

Data Formats

Docker

(use-package docker
  :ensure t
  :disabled)

(use-package dockerfile-mode
  :ensure t)

JSON

(use-package json-mode
  :ensure t
  :init
  (add-hook 'json-mode-hook '(lambda ()
                               (setq indent-tabs-mode nil)
                               (setq tab-width 4)
                               (setq indent-line-function (quote insert-tab))
                               (local-set-key (kbd "C-c C-f") 'json-pretty-print-buffer))))

(use-package json-reformat
  :ensure t
  :init
  (customize-set-variable 'json-reformat:indent-width 4))

XML

(use-package nxml-mode
  :mode "\\.xml\\'"
  :init
  (defun agg/xml-format ()
    "Format an XML buffer with xmllint."
    (interactive)
    (shell-command-on-region (point-min) (point-max)
                             "xmllint -format -"
                             (current-buffer) t
                             "*Xmllint Error Buffer*" t))
  (add-hook 'nxml-mode-hook 'agg/progmodes-hooks)
  :bind (:map nxml-mode-map
              ("C-c C-l" . agg/xml-format)))

Templating Languages

haml

(use-package haml-mode
  :ensure t
  :disabled)

Markdown

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "/opt/homebrew/bin/markdown"))

(add-to-list 'load-path (expand-file-name "~/.emacs.d/vendor/emacs-livedown"))
(require 'livedown)

Mustache

(use-package mustache-mode
  :ensure t
  :disabled)

DSLs

Cucumber

(use-package feature-mode
  :ensure t
  :disabled)

Puppet

(use-package puppet-mode
  :ensure t
  :disabled
  :init
  (add-to-list 'auto-mode-alist '("\\.pp$" . puppet-mode)))

SQL

(setq auto-mode-alist (cons '("\\.psql$" . sql-mode) auto-mode-alist))

(add-hook 'sql-mode-hook 'turn-off-auto-fill)
(add-hook 'sql-mode-hook 'agg/progmodes-hooks)

(provide 'agg-sql-mode)

Terrform

(use-package terraform-mode
  :ensure t)

YAML

(use-package yaml-mode
  :ensure t)

Languages

Treesitter Setup

;; Consider integrating tree-sitter-langs because it is a curated list
;; of langs and (presumably) working versions

(use-package tree-sitter
  :ensure t
  :init
  (setq major-mode-remap-alist
        '((bash-mode . bash-ts-mode)
          (cmake-mode . cmake-ts-mode)
          (css-mode . css-ts-mode)
          (elisp-mode . elisp-ts-mode)
          (elm-mode . elm-ts-mode) ;; not working because elm-ts-mode doesn't exist after this registration
          (enh-ruby-mode . ruby-ts-mode)
          (go-mode . go-ts-mode)
          (html-mode . html-ts-mode)
          (js2-mode . js-ts-mode)
          (json-mode . json-ts-mode)
          (make-mode . make-ts-mode)
          (markdown-mode . markdown-ts-mode)
          (python-mode . python-ts-mode)
          (ruby-mode . ruby-ts-mode)
          (tsx-mode . tsx-ts-mode)
          ;; (typescript-mode . typescript-ts-mode)  // LSP doesn't work with this on
          (yaml-mode . yaml-ts-mode))))

(use-package tree-sitter-langs
  :ensure t
  :init
  (setq treesit-language-source-alist
        '((bash "https://github.com/tree-sitter/tree-sitter-bash")
          (cmake "https://github.com/uyha/tree-sitter-cmake")
          (css "https://github.com/tree-sitter/tree-sitter-css")
          (elisp "https://github.com/Wilfred/tree-sitter-elisp")
          (elm "https://github.com/elm-tooling/tree-sitter-elm" "main" "src")
          (go "https://github.com/tree-sitter/tree-sitter-go")
          (html "https://github.com/tree-sitter/tree-sitter-html")
          (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
          (json "https://github.com/tree-sitter/tree-sitter-json")
          (make "https://github.com/alemuller/tree-sitter-make")
          (markdown "https://github.com/ikatyang/tree-sitter-markdown")
          (python "https://github.com/tree-sitter/tree-sitter-python")
          (ruby "https://github.com/tree-sitter/tree-sitter-ruby" "master" "src")
          (toml "https://github.com/tree-sitter/tree-sitter-toml")
          (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
          (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
          (yaml "https://github.com/ikatyang/tree-sitter-yaml"))))

;; Compiles all so you don't have to M-x treesit-install-language-grammar
;; (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))

Clojure

(use-package clojure-mode
  :ensure t
  :disabled
  :after (paredit)
  :init
  (add-hook 'clojure-mode-hook #'smartparens-mode))

;; avoid clojure-mode-extra-font-locking if using CIDER

(use-package cider
  :ensure t
  :disabled
  :init
  (setq clojure-indent-style :always-indent)
  (setq cider-repl-use-pretty-printing t)
  (setq cider-repl-wrap-history t)
  (setq cider-repl-history-size 1000)
  (setq cider-repl-history-file "~/.cider-repl-history.txt"))

(use-package flycheck-clojure
  :ensure t
  :disabled
  :after (flycheck)
  :config (flycheck-clojure-setup))

CSS

(customize-set-variable 'css-indent-offset 2)

Elm

(use-package elm-mode
  :ensure t)

Groovy

(use-package groovy-mode
  :ensure t
  :disabled)

HTML

(add-hook 'html-mode-hook 'turn-off-auto-fill)
(add-hook 'html-mode-hook 'agg/progmodes-hooks)

;; (use-package org-preview-html)

;; (use-package web-mode
;;   :ensure t
;;   :defer t)

Java

(add-hook 'java-mode-hook (lambda ()
                            (setq c-basic-offset 4
                                  tab-width 4)))

(use-package eclim
  :ensure t
  :disabled
  :init
  (setq eclimd-autostart nil)
  (setq eclim-eclipse-dirs '("/Applications/SpringToolSuite4.app/Contents/Eclipse"))
  (setq eclim-executable "/Applications/SpringToolSuite4.app/Contents/Eclipse/plugins/org.eclim_2.8.0/bin/eclim")
  (setq eclim-auto-save t)
  (setq eclim-use-yasnippet t)
  ;; display compilation error messages in the echo area
  (setq help-at-pt-display-when-idle t)
  (setq help-at-pt-timer-delay 0.1)
  (defun agg/java-mode-hook ()
    (eclim-mode t))
  (add-hook 'java-mode-hook 'agg/java-mode-hook)
  (add-hook 'java-mode-hook 'progmodes-hooks)
  :config
  (help-at-pt-set-timer))

Javascript

(setq js-indent-level 4)

(use-package add-node-modules-path
  :ensure t)

(use-package js2-mode
  :ensure t
  :mode
  "\\.js\\'"
  :after (smartparens add-node-modules-path)
  :init
  (setq js2-strict-missing-semi-warning nil)
  (setq js2-missing-semi-one-line-override nil)
  (add-hook 'js2-mode-hook 'progmodes-hooks)
  (add-hook 'js2-mode-hook #'smartparens-mode)
  (add-hook 'js2-mode-hook (lambda () (company-mode)))
  (add-hook 'js2-mode-hook (lambda () (setq js2-basic-offset 2)))
  (add-hook 'js2-mode-hook #'add-node-modules-path))

(use-package js2-refactor
  :ensure t
  :after (js2-mode)
  :init
  (setq js2-skip-preprocessor-directives t)
  (js2r-add-keybindings-with-prefix "C-c C-m")
  (add-hook 'js2-mode-hook #'js2-refactor-mode))

(use-package rjsx-mode
  :ensure t
  :disabled
  :mode
  "\\.jsx\\'"
  "\\.tsx\\'"
  :init
  (setq js2-strict-missing-semi-warning nil)
  (setq js2-missing-semi-one-line-override nil)
  (add-to-list 'interpreter-mode-alist '("node" . rjsx-mode))
  (add-hook 'rjsx-mode 'progmodes-hooks)
  (add-hook 'rjsx-mode (lambda () (setq js2-basic-offset 2))))

(use-package eslint-fix
  :ensure t)

(use-package eslintd-fix
  :ensure t)

(use-package react-snippets
  :ensure t
  :after (yasnippet))

Ruby

(defun enh-ruby-mode-before-save-hook ()
  (when (eq major-mode 'enh-ruby-mode)
    (message (current-buffer))))

(defun enh-ruby-mode-hooks ()
  "Hooks for ruby programming"
  (add-hook 'before-save-hook 'enh-ruby-mode-before-save-hook))

(use-package enh-ruby-mode
  :ensure t
  :init
  ;; automatically run rubocop autocorrect on save
  (setq rubocop-autocorrect-on-save t)
  ;; use enh-ruby-mode for these files
  (add-to-list 'auto-mode-alist
               '("\\(?:\\.rb\\|ru\\|rake\\|thor\\|jbuilder\\|gemspec\\|podspec\\|/\\(?:Gem\\|Rake\\|Cap\\|Thor\\|Vagrant\\|Guard\\|Pod\\)file\\)\\'" . enh-ruby-mode))
  (add-hook 'enh-ruby-mode-hook 'progmodes-hooks))

(use-package inf-ruby
  :ensure t
  :init
  (add-hook 'enh-ruby-mode-hook 'inf-ruby-minor-mode))

(use-package yari
  :ensure t
  ;; C-h R
  :init (define-key 'help-command "R" 'yari))

(use-package rubocop
  :ensure t
  :init
  (add-hook 'enh-ruby-mode-hook 'rubocop-mode))

(use-package robe
  :ensure t
  :after (enh-ruby-mode)
  :init
  (add-hook 'enh-ruby-mode-hook 'robe-mode))

(use-package ruby-tools
  :ensure t
  :init
  (add-hook 'enh-ruby-mode-hook 'ruby-tools-mode)
  :diminish ruby-tools-mode)

;; (use-package rbenv
;;   :ensure t
;;   :defer t
;;   :init
;;   (add-hook 'enh-ruby-mode-hook 'rbenv-use-corresponding)
;;   (global-rbenv-mode))

(use-package chruby
  :ensure t)

(use-package projectile-rails
  :ensure t
  :config
  (define-key projectile-rails-mode-map (kbd "C-c r") 'projectile-rails-command-map)
  (add-hook 'enh-ruby-mode-hook 'projectile-rails-mode))

Scala

(use-package scala-mode
  :ensure t
  :disabled
  :init
  (add-to-list 'auto-mode-alist '("\\.sbt$" . scala-mode))
  (add-hook 'scala-mode-hook 'progmodes-hooks)
  :interpreter ("scala" . scala-mode)) ;;  :pin melpa-stable

(use-package sbt-mode
  :ensure t
  :disabled) ;;:pin melpa-stable

(use-package ensime
  :ensure t
  :disabled
  :init
  (add-hook 'scala-mode-hook 'ensime-scala-mode-hook)) ;;:pin melpa-stable

;; (setq
;;  ensime-sbt-command "/home/agoodno/src/ccap3/sbt"
;;  sbt:program-name "/home/agoodno/src/ccap3/sbt"
;;  ensime-startup-notification nil)

Typescript

(use-package typescript-mode
  :ensure t
  :mode "\\.ts\\'"
  :hook (typescript-mode . lsp-deferred)
  :init
  (add-hook 'typescript-mode-hook 'agg/progmodes-hooks)
  :config
  (setq typescript-indent-level 4))

Embedded Clients

Email

(defun search-for-sender (msg)
  "Search for messages sent by the sender of the message at point."
  (mu4e-headers-search
   (concat "from:" (cdar (mu4e-message-field msg :from)))))

;; This [[https://jherrlin.github.io/posts/emacs-mu4e/][link]] was helpful for this setup
(use-package mu4e
  :ensure t
  :disabled
  :init
  (require 'smtpmail)
  (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu/mu4e")
  (setq mu4e-mu-binary "/usr/local/bin/mu"
        mu4e-get-mail-command "mbsync -q -a"
        mu4e-maildir (expand-file-name "~/Mail")
        mu4e-change-filenames-when-moving t
        mu4e-update-interval 300
        mu4e-index-update-in-background t
        mu4e-view-html-plaintext-ratio-heuristic most-positive-fixnum
        mu4e-sent-messages-behavior 'delete
        smtpmail-debug-info t
        smtpmail-stream-type 'starttls
        starttls-use-gnutls nil
        message-kill-buffer-on-exit t
        mu4e-attachment-dir "~/Downloads"
        mu4e-view-show-addresses t
        mu4e-html2text-command "textutil -stdin -format html -convert txt -stdout"
        shr-color-visible-luminance-min 5
        mu4e-split-view 'horizontal  ; 'vertical ; 'single-window
        mu4e-headers-visible-lines 16
        message-send-mail-function 'smtpmail-send-it)

  ;; define 'x' as the shortcut
  (add-to-list 'mu4e-view-actions
               '("xsearch for sender" . search-for-sender) t)
  :config
  (setq mu4e-contexts
        `( ,(make-mu4e-context
             :name "Gmail"
             :enter-func (lambda () (mu4e-message "Entering Gmail context"))
             :leave-func (lambda () (mu4e-message "Leaving Gmail context"))
             :match-func (lambda (msg)
                           (when msg
                             (mu4e-message-contact-field-matches msg
                                                                 :from "[email protected]")))
             :vars '((user-full-name . "Andrew Goodnough")
                     (user-mail-address . "[email protected]")
                     (smtpmail-smtp-server . "smtp.gmail.com")
                     (smtpmail-smtp-service . 587)
                     (smtpmail-smtp-user . "agoodno")
                     (mu4e-drafts-folder . "/gmail/drafts")
                     (mu4e-sent-folder . "/gmail/sent")
                     (mu4e-trash-folder . "/gmail/trash")
                     (mu4e-refile-folder . "/gmail/all")))
           ,(make-mu4e-context
             :name "iCloud"
             :enter-func (lambda () (mu4e-message "Entering iCloud context"))
             :leave-func (lambda () (mu4e-message "Leaving iCloud context"))
             :match-func (lambda (msg)
                           (when msg
                             (mu4e-message-contact-field-matches msg
                                                                 :from "[email protected]")))
             :vars '((user-full-name . "Andrew Goodnough")
                     (user-mail-address . "[email protected]")
                     (smtpmail-smtp-server . "smtp.mail.me.com")
                     (smtpmail-smtp-service . 587)
                     (smtpmail-smtp-user . "andrew.goodnough")
                     (mu4e-drafts-folder . "/icloud/drafts")
                     (mu4e-sent-folder . "/icloud/sent")
                     (mu4e-trash-folder . "/icloud/trash")
                     (mu4e-refile-folder . "/icloud/archive")))))
  (add-hook 'mu4e-view-mode-hook (lambda () (setq truncate-lines t))))

IRC

;; (defvar freenode-password "")
;; (defvar bitlbee-password "")

(setq
 erc-server "irc.wicourts.gov"
 ;; erc-server "chat.freenode.net"
 erc-nick "agoodno"
 erc-prompt (lambda () (concat "[" (buffer-name) "]"))
 ;; erc-prompt-for-nickserv-password nil
 ;; erc-nickserv-passwords `((freenode ("agoodno" . ,freenode-password)))
 erc-email-userid "[email protected]"
 ;; erc-email-userid "[email protected]"
 erc-user-full-name user-full-name
 ;; erc-autojoin-channels-alist '(("irc.wicourts.gov" "#ccap3" "#cc"))
 erc-autojoin-channels-alist
 '(("freenode.net" "#emacs" "#elasticsearch")
   ("wicourts.gov" "#ccap3" "#cc"))
 ;; erc-join-buffer 'bury
 erc-hide-list '("QUIT" "JOIN" "KICK" "NICK" "MODE")
 erc-echo-notices-in-minibuffer-flag t
 erc-auto-query 'buffer
 erc-save-buffer-on-part nil
 erc-save-queries-on-quit nil
 erc-log-write-after-send t
 erc-log-write-after-insert t
 erc-fill-column 75
 erc-header-line-format nil
 erc-track-exclude-types '("324" "329" "332" "333" "353" "477" "MODE"
                           "JOIN" "PART" "QUIT" "NICK")
 ;; erc-lurker-threshold-time 3600
 ;; erc-track-priority-faces-only t
 ;; erc-autojoin-timing :ident
 ;; erc-flood-protect nil
 ;; erc-server-send-ping-interval 45
 ;; erc-server-send-ping-timeout 180
 ;; erc-server-reconnect-timeout 60
 ;; erc-server-flood-penalty 1000000
 ;; erc-accidental-paste-threshold-seconds 0.5
 erc-fill-function 'erc-fill-static
 erc-fill-static-center 14)

(defun freenode-connect ()
  "Connect to freenode."
  (interactive)
  (erc :server "irc.freenode.net" :port 6667 :nick "agoodno"))

(defun bitlbee-connect ()
  "Connect to bitlbee."
  (interactive)
  (erc :server "127.0.0.1" :port 6667))

(defun wicourts-connect ()
  "Connect to wicourts."
  (interactive)
  (erc :server "irc.wicourts.gov" :port 6667 :nick "agoodno"))

;;(add-hook 'erc-join-hook 'bitlbee-identify)

(defun bitlbee-identify ()
  "If we're on the bitlbee server, send the identify command to the &bitlbee channel."
  (when (and (string= "127.0.0.1" erc-session-server)
             (string= "&bitlbee" (buffer-name)))
    (erc-message "PRIVMSG" (format "%s identify %s"
                                   (erc-default-target)
                                   bitlbee-password))))

;; (delete 'erc-fool-face 'erc-track-faces-priority-list)
;; (delete '(erc-nick-default-face erc-fool-face) 'erc-track-faces-priority-list)

;; (eval-after-load 'erc
;;   '(progn
;;      ;; (when (not (package-installed-p 'erc-hl-nicks))
;;      ;;   (package-install 'erc-hl-nicks))
;;      (require 'erc-spelling)
;;      (require 'erc-services)
;;      (require 'erc-truncate)
;;      ;; (require 'erc-hl-nicks)
;;      (require 'notifications)
;;      (erc-services-mode 1)
;;      (erc-truncate-mode 1)
;;      (setq erc-complete-functions '(erc-pcomplete erc-button-next))
;;      ;; (add-to-list 'erc-modules 'hl-nicks)
;;      (add-to-list 'erc-modules 'spelling)
;;      (set-face-foreground 'erc-input-face "dim gray")
;;      (set-face-foreground 'erc-my-nick-face "blue")
;;      (define-key erc-mode-map (kbd "C-c r") 'pnh-reset-erc-track-mode)
;;      (define-key erc-mode-map (kbd "C-c C-M-SPC") 'erc-track-clear)
;;      (define-key erc-mode-map (kbd "C-u RET") 'browse-last-url-in-brower)))

;; (defun erc-track-clear ()
;;   (interactive)
;;   (setq erc-modified-channels-alist nil))

;; (defun browse-last-url-in-brower ()
;;   (interactive)
;;   (require 'ffap)
;;   (save-excursion
;;     (let ((ffap-url-regexp "\\(https?://\\)."))
;;       (ffap-next-url t t))))

;; (defun pnh-reset-erc-track-mode ()
;;   (interactive)
;;   (setq erc-modified-channels-alist nil)
;;   (erc-modified-channels-update)
;;   (erc-modified-channels-display))

;; (require 'erc-services)
;; (erc-services-mode 1)

;; ;;; Notify me when a keyword is matched (someone wants to reach me)

;; (defvar my-erc-page-message "%s says %s"
;;   "Format of message to display in dialog box")

;; (defvar my-erc-page-nick-alist nil
;;   "Alist of nicks and the last time they tried to trigger a notification")

;; (defvar my-erc-page-timeout 60
;;   "Number of seconds that must elapse between notifications from the same person.")

;; (defun my-erc-page-popup-notification (message)
;;   (when window-system
;;     ;; must set default directory, otherwise start-process is unhappy
;;     ;; when this is something remote or nonexistent
;;     (let ((default-directory "~/"))
;;       ;; 8640000 milliseconds = 1 day
;;       (start-process "page-me" nil "notify-send"
;;                      "-u" "normal" "-t" "8640000" "ERC"
;;                      (format my-erc-page-message (car (split-string nick "!")) message)))))

;; (defun my-erc-page-allowed (nick &optional delay)
;;   "Return non-nil if a notification should be made for NICK.
;; If DELAY is specified, it will be the minimum time in seconds
;; that can occur between two notifications.  The default is
;; `my-erc-page-timeout'."
;;   (unless delay (setq delay my-erc-page-timeout))
;;   (let ((cur-time (time-to-seconds (current-time)))
;;         (cur-assoc (assoc nick my-erc-page-nick-alist))
;;         (last-time))
;;     (if cur-assoc
;;         (progn
;;           (setq last-time (cdr cur-assoc))
;;           (setcdr cur-assoc cur-time)
;;           (> (abs (- cur-time last-time)) delay))
;;       (push (cons nick cur-time) my-erc-page-nick-alist)
;;       t)))

;; (defun my-erc-page-me (match-type nick message)
;;   "Notify the current user when someone sends a message that
;; matches a regexp in `erc-keywords'."
;;   (interactive)
;;   (when (and (eq match-type 'keyword)
;;              ;; I don't want to see anything from the erc server
;;              (null (string-match "\\`\\([sS]erver\\|localhost\\)" nick))
;;              ;; or bots
;;              (null (string-match "\\(bot\\|serv\\)!" nick))
;;              ;; or from those who abuse the system
;;              (my-erc-page-allowed nick))
;;     (my-erc-page-popup-notification message)))
;; (add-hook 'erc-text-matched-hook 'my-erc-page-me)

;; (defun my-erc-page-me-PRIVMSG (proc parsed)
;;   (let ((nick (car (erc-parse-user (erc-response.sender parsed))))
;;         (target (car (erc-response.command-args parsed)))
;;         (msg (erc-response.contents parsed)))
;;     (when (and (erc-current-nick-p target)
;;                (not (erc-is-message-ctcp-and-not-action-p msg))
;;                (my-erc-page-allowed nick))
;;       (my-erc-page-popup-notification msg)
;;       nil)))
;; (add-hook 'erc-server-PRIVMSG-functions 'my-erc-page-me-PRIVMSG)

;; (eval-after-init
;;  '(and
;;                                         ; (add-to-list 'erc-modules 'autoaway)
;;    (add-to-list 'erc-modules 'autojoin)
;;    (add-to-list 'erc-modules 'button)
;;    (add-to-list 'erc-modules 'completion)
;;    (add-to-list 'erc-modules 'fill)
;;    (add-to-list 'erc-modules 'irccontrols)
;;    (add-to-list 'erc-modules 'list)
;;    (add-to-list 'erc-modules 'log)
;;    (add-to-list 'erc-modules 'match)
;;    (add-to-list 'erc-modules 'menu)
;;    (add-to-list 'erc-modules 'move-to-prompt)
;;    (add-to-list 'erc-modules 'netsplit)
;;    (add-to-list 'erc-modules 'networks)
;;    (add-to-list 'erc-modules 'noncommands)
;;    (add-to-list 'erc-modules 'notify)
;;    (add-to-list 'erc-modules 'readonly)
;;    (add-to-list 'erc-modules 'ring)
;;    (add-to-list 'erc-modules 'stamp)
;;    (add-to-list 'erc-modules 'track )
;;    (erc-update-modules)))

;; (customize-set-variable 'erc-server "irc.freenode.net")
;; (customize-set-variable 'erc-port 6667)
;; (customize-set-variable 'erc-nick "agoodno")

;; (use-package erc-hipchatify
;;   :ensure t
;;   :defer t
;;   :init
;;   (progn
;;     ;; (customize-set-variable 'shr-use-fonts f)
;;     ;; (customize-set-variable 'shr-external-browser "")
;;     (add-to-list 'erc-modules 'hipchatify)
;;     (erc-update-modules)))

Slack

;; How to get a token
;; Run this command to describe the process of saving token and cookie
;; M-x slack-refresh-token

;; The short version is to grab:
;; token xoxc-XXXX
;; d    xoxd-YYYY
;; d-s  123
;; lc   456
;; combine to make:
;; xoxd-YYYY; d-s=123; lc=456

;; See this to get it working with enterprise tokens:
;; https://github.com/emacs-slack/emacs-slack/commit/472c48673f21148d85c069e8f404584349e780ca

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

;; == ~/.authinfo contents ==
;; machine clojurians.slack.com user token password "xoxc-XXX"
;; machine clojurians.slack.com user cookie password "xoxd-YYY; d-s=123; lc=456"
;; machine elmlang.slack.com user token password "xoxc-ZZZ"
;; machine elmlang.slack.com user cookie password "xoxd-zzz; d-s-789; lc=012"

(use-package slack
  :ensure t
  :commands (slack-start)
  :init
  (setq slack-buffer-emojify t)
  (setq slack-prefer-current-team t)
  (setq auth-source-debug 'trivia)
  :bind (("C-c S S" . slack-start)
         ("C-c S K" . slack-stop)
         ("C-c S c" . slack-select-rooms)
         ("C-c S u" . slack-select-unread-rooms)
         ("C-c S U" . slack-user-select)
         ("C-c S s" . slack-search-from-messages)
         ("C-c S J" . slack-jump-to-browser)
         ("C-c S j" . slack-jump-to-app)
         ("C-c S e" . slack-insert-emoji)
         ("C-c S E" . slack-message-edit)
         ("C-c S r" . slack-message-add-reaction)
         ("C-c S t" . slack-thread-show-or-create)
         ("C-c S g" . slack-message-redisplay)
         ("C-c S G" . slack-conversations-list-update-quick)
         ("C-c S q" . slack-quote-and-reply)
         ("C-c S Q" . slack-quote-and-reply-with-link)
         (:map slack-mode-map
               (("@" . slack-message-embed-mention)
                ("#" . slack-message-embed-channel)))
         (:map slack-thread-message-buffer-mode-map
               (("C-c '" . slack-message-write-another-buffer)
                ("@" . slack-message-embed-mention)
                ("#" . slack-message-embed-channel)))
         (:map slack-message-buffer-mode-map
               (("C-c '" . slack-message-write-another-buffer)))
         (:map slack-message-compose-buffer-mode-map
               (("C-c '" . slack-message-send-from-buffer))))
  :config
  (slack-register-team
   :name "elmlang"
   :default t
   :full-and-display-names t
   :subscribed-channels '(beginners)
   :token (auth-source-pick-first-password
           :host "elmlang.slack.com"
           :user "token"
           :type 'netrc
           :max 1)
   :cookie (auth-source-pick-first-password
            :host "elmlang.slack.com"
            :user "cookie"
            :type 'netrc
            :max 1))
  (slack-register-team
   :name "clojurians"
   :full-and-display-names t
   :subscribed-channels '(announcements beginners clojurescript)
   :token (auth-source-pick-first-password
           :host "clojurians.slack.com"
           :user "token"
           :type 'netrc
           :max 1)
   :cookie (auth-source-pick-first-password
            :host "clojurians.slack.com"
            :user "cookie"
            :type 'netrc
            :max 1)))
;; (slack-register-team
;;  :name "zendesk"
;;  :token (auth-source-pick-first-password
;;          :host "zendesk.slack.com"
;;          :user "[email protected]")
;;  :cookie (auth-source-pick-first-password
;;           :host "zendesk.slack.com"
;;           :user "[email protected]^cookie")
;;  :subscribed-channels '((sunburst-build-deploy sunburst-eng-p tood-directs-p))))

(use-package alert
  :commands (alert)
  :init
  (setq alert-default-style 'notifier))

Verb REST client

(use-package verb
  :ensure t
  :config
  (define-key org-mode-map (kbd "C-c C-r") verb-command-map))

(use-package impostman
  :ensure t)

Packages

browse-url

;; Open links in Chrome on macOS
;; (setq gnus-button-url 'browse-url-generic
;;       browse-url-generic-program "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
;;       browse-url-browser-function gnus-button-url)

;; Open links in Safari
(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "open")

f

(use-package f
  :ensure t)

flycheck

(use-package flycheck
  :ensure t
  :init
  ;; (setq flycheck-javascript-eslint-executable "~/work/wastewitness/node_modules/.bin/eslint")
  ;; (setq flycheck-javascript-standard-executable "~/work/wastewitness/node_modules/.bin/standard")
  (setq-default flycheck-disabled-checkers
                '(emacs-lisp-checkdoc))
  (setq-default flycheck-disabled-checkers
                (append flycheck-disabled-checkers
                        '(javascript-jshint)))
  (setq-default flycheck-disabled-checkers
                (append flycheck-disabled-checkers
                        '(json-jsonlist)))
  (global-flycheck-mode))

ledger

(use-package ledger-mode
  :ensure t
  :disabled
  :init
  (add-to-list 'auto-mode-alist '("\\.dat$" . ledger-mode)))

magit

(use-package magit
  :ensure t
  :init
  (customize-set-variable 'magit-display-buffer-function
                          (quote magit-display-buffer-fullframe-status-v1))
  (customize-set-variable 'magit-status-sections-hook
                          '(magit-insert-status-headers
                            magit-insert-merge-log
                            magit-insert-rebase-sequence
                            magit-insert-am-sequence
                            magit-insert-sequencer-sequence
                            magit-insert-bisect-output
                            magit-insert-bisect-rest
                            magit-insert-unpulled-from-upstream
                            magit-insert-unpulled-from-pushremote
                            magit-insert-unpushed-to-upstream
                            magit-insert-unpushed-to-pushremote
                            magit-insert-staged-changes
                            magit-insert-unstaged-changes
                            magit-insert-untracked-files
                            magit-insert-stashes))
  (customize-set-variable 'magit-repolist-columns
                          (quote
                           (("Name" 40 magit-repolist-column-ident nil)
                            ("Path" 99 magit-repolist-column-path))))
  (global-set-key (kbd "C-c g") 'magit-status)
  (global-set-key (kbd "C-c h") 'magit-list-repositories))

pdf-tools

(use-package pdf-tools
  :ensure t
  :disabled
  :init
  (pdf-tools-install))

projectile

(use-package projectile
  :ensure t
  :config
  (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
  (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
  (projectile-mode +1))

saveplace

(setq save-place-file (locate-user-emacs-file "places" ".emacs-places"))

(save-place-mode 1)

shell-mode

;; Some ideas from: https://www.reddit.com/r/emacs/comments/9x2st8/disable_all_colours_in_shell_mode/

;; maybe turn off colors altogether
;; (setq ansi-color-for-comint-mode 'filter)

;; shell-mode hooks

;; Add color to a shell running in emacs 'M-x shell'
(autoload 'ansi-color-for-comint-mode-on "ansi-color" nil t)
(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)

(add-hook 'shell-mode-hook '(lambda () (toggle-truncate-lines 1)))

;; comint-mode hooks
(defun agg/my-comint-init ()
  ;; Stops echo of command
  (setq comint-process-echoes t)
  ;; The default font lock rules can be expensive and cause hangs
  ;; on long lines but this doesn't disable font-lock completely
  ;; because I like having the prompt highlighted.
  (setq shell-font-lock-keywords nil)
  ;; Makes the prompt read-only running in emacs 'M-x shell'
  (setq comint-prompt-read-only t))
(add-hook 'comint-mode-hook 'agg/my-comint-init)

tidy

(setq tidy-shell-command "/usr/local/bin/tidy")
(setq tidy-config-file "~/.tidyrc")
(setq tidy-temp-directory "/tmp")

tramp

(setq tramp-default-method "ssh")

(defun connect-patproc-test ()
  (interactive)
  (dired "/[email protected]:/opt/patproc-test/"))

unfill

(use-package unfill
  :ensure t)

uniquify

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

Startup

(use-package server
  :config
  (unless (and (fboundp 'server-running-p) (server-running-p))
    (message "Starting Emacs server...")
    (setq server-client-instructions nil)
    (server-start)))

Notes

Clean test

Occasionally, I like to test my init files from a clean environment so I know I haven’t messed something up along the way. To do this, I do the following:

  1. Close Emacs
  2. Checkout the git revision you think should work (start with main)
  3. Clean the existing ELPA compiled directory

    ~/src/dotemacs $ rm -rf elpa

  4. Start Emacs

    Repeat all steps until you get a clean launch. If you don’t get a clean start, go back to a previous revision in the git log until you do.

    It would be nice to have something like a build server that would perform a “clean build” on all new configuration changes.

> >

Future packages

About

My emacs config

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published