#+TITLE:  Emacs Configuration
#+PROPERTY: header-args:emacs-lisp :tangle ~/dotfiles/.config/emacs/init.el :mkdirp yes :lexical yes

* Configuration
:PROPERTIES:
:TOC:      :include all :depth 3 :force (depth) :ignore (this) :local (depth)
:END:
:CONTENTS:
- [[#configuration][Configuration]]
  - [[#package-management][Package Management]]
  - [[#general][General]]
  - [[#theme][Theme]]
  - [[#fonts][Fonts]]
  - [[#keybinding][Keybinding]]
  - [[#completion-frameworks][Completion Frameworks]]
  - [[#buffer--workspaces][Buffer & Workspaces]]
  - [[#file-browser][File Browser]]
  - [[#org][Org]]
  - [[#programmingeditor][Programming/Editor]]
- [[#tools][Tools]]
  - [[#magit][Magit]]
  - [[#eshell][Eshell]]
  - [[#vterm][Vterm]]
  - [[#pdf][PDF]]
  - [[#e-mail][E-Mail]]
  - [[#irc][IRC]]
:END:
** Package Management
I use [[https://github.com/raxod502/straight.el][straight.el]] for managing packages as it's fairly simple and robust. At some point I'd like to move to primarily using Guix for Emacs package management.
#+begin_src emacs-lisp
(setq package-enable-at-startup nil)

;; * Package Management with straight.el
(unless (featurep 'straight)
  ;; Bootstrap straight.el
  (defvar bootstrap-version)
  (let ((bootstrap-file
         (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
        (bootstrap-version 5))
    (unless (file-exists-p bootstrap-file)
      (with-current-buffer
          (url-retrieve-synchronously
           "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
           'silent 'inhibit-cookies)
        (goto-char (point-max))
        (eval-print-last-sexp)))
    (load bootstrap-file nil 'nomessage)))

;; Use straight.el for use-package expressions
(straight-use-package 'use-package)

(setq package-archives '(("melpa" . "https://melpa.org/packages/")
                         ("org" . "https://orgmode.org/elpa/")
                         ("elpa" . "https://elpa.gnu.org/packages/")))
#+end_src

** General
*** User Interface
#+begin_src emacs-lisp
(scroll-bar-mode -1)                 ;; Disable scroll bar
(tool-bar-mode -1)                   ;; Disable tool bar menu
(tooltip-mode -1)                    ;; Disable tooltips
(set-fringe-mode 10)                 ;; Disable fringe mode
(menu-bar-mode -1)                   ;; Disable menu bar
(global-display-line-numbers-mode t) ;; Display line numbers
(set-default 'truncate-lines nil)    ;; Wrap lines at end of screen
(setq visible-bell t)                ;; Visible flash to represent a bell
(setq x-select-enable-clipboard t)   ;; Enable clipboard
(global-tab-line-mode -1)            ;; Disable tab mode
(setq inhibit-startup-message t)     ;; What it says
(setq global-auto-revert-non-file-buffers t)
(global-auto-revert-mode 1)          ;; Revert all buffers on change
(setq custom-safe-themes t)          ;; Treat themes as safe

;; Disable line mode for specific major/minor modes.
(dolist (mode '(org-mode-hook
                vterm-mode-hook
                term-mode-hook
                eshell-mode-hook))
  (add-hook mode (lambda () (display-line-numbers-mode 0))))
#+end_src
*** Transparency
#+begin_src emacs-lisp
(set-frame-parameter (selected-frame) 'alpha '(90 . 90))
(add-to-list 'default-frame-alist '(alpha . (90 . 90)))
(set-frame-parameter (selected-frame) 'fullscreen 'maximized)
(add-to-list 'default-frame-alist '(fullscreen . maximized))
#+end_src

*** Modeline
Doom's modeline is excellent, and much less cluttered than the default Emacs modeline.
#+begin_src emacs-lisp
(use-package doom-modeline
  :straight t
  :init (doom-modeline-mode 1)
  :custom ((doom-modeline-height 15)))

;; install all-the-icons when first loading the emacs conf
;; remember to run M-x all-the-icons-install-fonts
(use-package all-the-icons
  :straight t
  :if (display-graphic-p))
#+end_src

*** Keybinding display
Which-key is a package that displays the available commands granted that it's incomplete. For instance, if I type "SPC", I will see all of the commands available to the leader key.

#+begin_src emacs-lisp
(use-package which-key
  :straight t
  :defer 0
  :diminish which-key-mode
  :config
  (which-key-mode)
  (setq which-key-idle-delay 0.3))
#+end_src

*** Notifications
**** Alerts
Minimal alerts so I can see notifications from various programs in Emacs.

#+begin_src emacs-lisp
(use-package alert
  :straight t
  :config
  (setq alert-default-style 'notification))
#+end_src
**** Disable Warnings
#+begin_src emacs-lisp
(setq large-file-warning-threshold nil) ;; Disables warnings for large files
(setq vc-follow-symlinks t)             ;; Disables warnings for symlinks
#+end_src
*** Helpful Describe Functionality
Helpful.el is a package that reformats the describe-X functions to be much more informative.

#+begin_src emacs-lisp
(use-package helpful
  :straight t
  :commands (helpful-callable helpful-variable helpful-command helpful-key))
#+end_src
*** Autosaves
#+begin_src emacs-lisp
(use-package super-save
  :straight t
  :config 
  (super-save-mode +1))

;; Auto reverting Changed Files
(setq global-auto-revert-non-file-buffers nil)
(global-auto-revert-mode -1)
#+end_src
*** Pinentry
#+begin_src emacs-lisp
(use-package pinentry :straight t)
(setq epa-pinentry-mode 'loopback)
(pinentry-start)
#+end_src
*** Tramp
#+begin_src emacs-lisp
(setq tramp-default-method "ssh")
#+end_src
*** Pick up that can!
#+begin_src emacs-lisp
(use-package no-littering
  :straight t)
#+end_src
** Theme

#+begin_src emacs-lisp
;; (use-package modus-themes
;;   :straight t
;;   :init
;;   (setq modus-themes-italic-constructs t
;;         modus-themes-bold-constructs nil
;;         modus-themes-region '(accented bg-only no-extend)
;;         modus-themes-org-blocks 'greyscale
;;         modus-themes-paren-match 'intense
;;         modus-themes-mixed-fonts t)
;;   (modus-themes-load-themes)
;;   :config
;;   (modus-themes-load-vivendi)
;;   :bind
;;   ("<f5>" . modus-themes-toggle))

;; Doing some doom themes for a while.
(use-package doom-themes
  :straight t
  :init
  (load-theme 'doom-Iosvkem))
#+end_src

** Fonts

#+begin_src emacs-lisp
(set-face-attribute 'default nil :font "Iosevka Term" :height 130 :weight 'medium)
(set-face-attribute 'variable-pitch nil :font "Iosevka" :height 1.0 :weight 'medium)
(set-face-attribute 'fixed-pitch nil :font "Iosevka Term" :height 1.0 :weight 'medium)

(use-package mixed-pitch
  :straight t
  :config
  (add-hook 'org-mode-hook  #'mixed-pitch-mode))
(use-package unicode-fonts :straight t)
#+end_src

** Keybinding
I came from vim, so naturally I use Evil keybindings. I also used to use Doom Emacs before rolling my own configuration so I've borrowed some ideas from  my time spent there as well. Between the two paradigms I rarely leave the home row which is ideal for typing for extended periods of time. 

*** General.el (leader-key keybindings)
I really enjoy having a leader key and eschewing the Control and Alt keys entirely. Doom had far too many leader keybindings that I never used so I've boiled them down to the essentials that I used the most.

#+begin_src emacs-lisp
(global-set-key (kbd "<escape>") 'keyboard-escape-quit) ;; Escape exits out of prompts

;; Define Leader Key
(use-package general
  :straight t
  :config
  (general-evil-setup t)
  (general-create-definer opal/leader-keys
    :keymaps '(normal insert visual emacs)
    :prefix "SPC"
    :global-prefix "C-SPC")
  ;; Define Keybindings (potentially move into own file)
  (opal/leader-keys
    ;; Leader-map
    ";" #'pp-eval-expression        ;; Eval expression
    ":" #'execute-extended-command  ;; Eq to M-x
    "u" #'universal-argument        ;; Universal argument
    "w" #'evil-window-map           ;; Window functions
    "." #'find-file                 ;; Find file (opens in mini-buffer)
    "," #'consult-buffer            ;; Switch to buffer
    "<" #'switch-to-buffer          ;; Switch to non-perspective.el minibuffer
    "s" #'consult-line
    "p" #'projectile-command-map
    "l" #'lsp-map

    ;; Magit
    "gg" #'magit-status
    "gi" #'magit-init
    "gc" #'magit-clone
    
    ;; Workspaces
    "bi"   #'persp-ibuffer
    "bl"   #'persp-next
    "bh"   #'persp-prev
    "br"   #'persp-rename
    "bD"   #'persp-remove-buffer
    "bd"   #'kill-buffer

    ;; Terminal/Shell
    "tt" #'vterm
    "te" #'eshell

    ;; Org
    ;; Babel
    "obt" #'org-babel-tangle
    ;; Gen
    "ol" #'org-insert-link
    "or" #'org-reload
    ;; Agenda
    "oa" #'org-agenda

    ;; Eval
    "eb" #'eval-buffer
    "er" #'eval-region
    "ef" #'eval-defun
    "el" #'eval-last-sexp

    ;; Describe
    "df" #'describe-function
    "dv" #'describe-variable
    "ds" #'describe-symbol
    "dk" #'describe-key
    "dp" #'describe-package))
#+end_src

*** Evil
#+begin_src emacs-lisp
(use-package evil
  :straight t
  :init
  (setq evil-want-integration t)
  (setq evil-want-keybinding nil)
  (setq evil-want-C-u-scroll t)
  (setq evil-want-C-i-jump nil)
  (setq evil-respect-visual-line-mode t)
  :config
  (evil-mode 1)
  (define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
  (define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join)

  ;; Use visual line motions even outside of visual-line-mode buffers
  (evil-global-set-key 'motion "j" 'evil-next-visual-line)
  (evil-global-set-key 'motion "k" 'evil-previous-visual-line)

  (evil-set-initial-state 'messages-buffer-mode 'normal)
  (evil-set-initial-state 'dashboard-mode 'normal))

(use-package evil-collection
  :straight t
  :after evil
  :config
  (evil-collection-init))
#+end_src

** Completion Frameworks
*** Vertico
#+begin_src emacs-lisp
;; Enable vertico
(use-package vertico
  :straight t
  :bind
  (:map vertico-map
        ("C-j" . vertico-next)
        ("C-k" . vertico-previous))
  :init
  (vertico-mode)
  (setq vertico-scroll-margin 0) ;; Scroll margin
  (setq vertico-count 10)        ;; Candidates
  (setq vertico-cycle t))        ;; Enable cycling

  ;; (use-package ivy
  ;;   :straight t
  ;;   :bind (("C-s" . 'swiper)
  ;;          :map ivy-minibuffer-map
  ;;          ("TAB" . ivy-alt-done)
  ;;          ("C-l" . ivy-alt-done)
  ;;          ("C-j" . ivy-next-line)
  ;;          ("C-k" . ivy-previous-line)
  ;;          :map ivy-switch-buffer-map
  ;;          ("C-k" . ivy-previous-line)
  ;;          ("C-l" . ivy-done)
  ;;          ("C-d" . ivy-reverse-i-search-kill))
  ;;   :config
  ;;   (ivy-mode 1))

  ;; (use-package ivy-rich
  ;;   :straight t
  ;;   :after ivy
  ;;   :init (ivy-rich-mode 1))
#+end_src
*** Orderless
Completion style
#+begin_src emacs-lisp
(use-package orderless
  :straight t
  :init
  (setq completion-styles '(orderless)
        completion-category-defaults nil
        completion-category-overrides '((file (styles partial-completion)))))
#+end_src

*** History
Persistent history
#+begin_src emacs-lisp
(use-package savehist
  :straight t
  :init
  (savehist-mode))
#+end_src

*** Consult
#+begin_src emacs-lisp
(use-package consult
  :straight t
  :defer t
  :config
  (setq
        consult-narrow-key "<"
        consult-line-numbers-widen t
        consult-async-min-input 2
        consult-async-refresh-delay  0.15
        consult-async-input-throttle 0.2
        consult-async-input-debounce 0.1))
;; (use-package counsel
;;   :straight t
;;   :bind
;;   (("C-M-j" . 'counsel-switch-buffer)
;;    :map minibuffer-local-map
;;    ("C-r" . 'counsel-minibuffer-history))

;;   :custom
;;   (counsel-linux-app-format-function #'counsel-linux-app-format-function-name-only)
;;   :config
;;   (counsel-mode 1))
#+end_src

*** Marginalia
Marginalia provides annotations next to minibuffer items.

#+begin_src emacs-lisp
(use-package marginalia
  :straight t
  :bind (("M-A" . marginalia-cycle)
         :map minibuffer-local-map
         ("M-A" . marginalia-cycle))
  :init
  (marginalia-mode))
#+end_src

*** Company
#+begin_src emacs-lisp
(use-package company
  :straight t
  :commands (company-complete-common
             company-complete-common-or-cycle
             company-manual-begin
             company-grab-line)
  :init
  (setq company-minimum-prefix-length 2
        company-tooltip-limit 14
        company-tooltip-align-annotations t
        company-require-match 'never
        company-global-modes
        '(not erc-mode
              message-mode
              help-mode
              vterm-mode)
        company-frontends
        '(company-pseudo-tooltip-frontend  ; always show candidates in overlay tooltip
          company-echo-metadata-frontend)  ; show selected candidate docs in echo area
        company-backends '(company-capf)
        company-auto-commit nil
        company-dabbrev-other-buffers nil
        company-dabbrev-ignore-case nil
        company-dabbrev-downcase nil))
#+end_src

** Buffer & Workspaces
*** Perspective
#+begin_src emacs-lisp
(use-package perspective
  :straight t
  :config
  (persp-mode))
#+end_src
*** Bufler
Helps organize buffers in groups. 
#+begin_src emacs-lisp
(use-package bufler :straight t)
#+end_src
*** Winner
Allows window undo
#+begin_src emacs-lisp
(use-package winner
  :straight t)
#+end_src
** File Browser
I don't even think I've heard of an alternative to Dired...
#+begin_src emacs-lisp
(use-package dired
  :init
  (setq dired-auto-revert-buffer t
        dired-dwim-target t
        dired-hide-details-hide-symlink-targets nil
        dired-recursive-copies 'always
        dired-recursive-deletes 'top
        dired-create-destination-dirs 'ask))

(use-package dired-rsync
  :straight t
  :general (dired-mode-map "C-c C-r" #'dired-rsync))
(use-package all-the-icons-dired :straight t)
(use-package dired-single :straight t)
(use-package dired-collapse :straight t)

;; Make pretty colors!
(use-package dired-rainbow
  :straight t
  :after dired
  :config
  (dired-rainbow-define-chmod directory "#6cb2eb" "d.*")
  (dired-rainbow-define html "#eb5286" ("css" "less" "sass" "scss" "htm" "html" "jhtm" "mht" "eml" "mustache" "xhtml"))
  (dired-rainbow-define xml "#f2d024" ("xml" "xsd" "xsl" "xslt" "wsdl" "bib" "json" "msg" "pgn" "rss" "yaml" "yml" "rdata"))
  (dired-rainbow-define document "#9561e2" ("docm" "doc" "docx" "odb" "odt" "pdb" "pdf" "ps" "rtf" "djvu" "epub" "odp" "ppt" "pptx"))
  (dired-rainbow-define markdown "#ffed4a" ("org" "etx" "info" "markdown" "md" "mkd" "nfo" "pod" "rst" "tex" "textfile" "txt"))
  (dired-rainbow-define database "#6574cd" ("xlsx" "xls" "csv" "accdb" "db" "mdb" "sqlite" "nc"))
  (dired-rainbow-define media "#de751f" ("mp3" "mp4" "mkv" "MP3" "MP4" "avi" "mpeg" "mpg" "flv" "ogg" "mov" "mid" "midi" "wav" "aiff" "flac"))
  (dired-rainbow-define image "#f66d9b" ("tiff" "tif" "cdr" "gif" "ico" "jpeg" "jpg" "png" "psd" "eps" "svg"))
  (dired-rainbow-define log "#c17d11" ("log"))
  (dired-rainbow-define shell "#f6993f" ("awk" "bash" "bat" "sed" "sh" "zsh" "vim"))
  (dired-rainbow-define interpreted "#38c172" ("py" "ipynb" "rb" "pl" "t" "msql" "mysql" "pgsql" "sql" "r" "clj" "cljs" "scala" "js"))
  (dired-rainbow-define compiled "#4dc0b5" ("asm" "cl" "lisp" "el" "c" "h" "c++" "h++" "hpp" "hxx" "m" "cc" "cs" "cp" "cpp" "go" "f" "for" "ftn" "f90" "f95" "f03" "f08" "s" "rs" "hi" "hs" "pyc" ".java"))
  (dired-rainbow-define executable "#8cc4ff" ("exe" "msi"))
  (dired-rainbow-define compressed "#51d88a" ("7z" "zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar" "war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar"))
  (dired-rainbow-define packaged "#faad63" ("deb" "rpm" "apk" "jad" "jar" "cab" "pak" "pk3" "vdf" "vpk" "bsp"))
  (dired-rainbow-define encrypted "#ffed4a" ("gpg" "pgp" "asc" "bfe" "enc" "signature" "sig" "p12" "pem"))
  (dired-rainbow-define fonts "#6cb2eb" ("afm" "fon" "fnt" "pfb" "pfm" "ttf" "otf"))
  (dired-rainbow-define partition "#e3342f" ("dmg" "iso" "bin" "nrg" "qcow" "toast" "vcd" "vmdk" "bak"))
  (dired-rainbow-define vc "#0074d9" ("git" "gitignore" "gitattributes" "gitmodules"))
  (dired-rainbow-define-chmod executable-unix "#38c172" "-.*x.*"))
#+end_src
** Org
*** General Configuration
#+begin_src emacs-lisp
(defun opal/org-font-setup ()
  ;; Replace list hyphen with dot
  (font-lock-add-keywords 'org-mode
                          '(("^ *\\([-]\\) "
                             (0 (prog1 () (compose-region (match-beginning 1) (match-end 1)
                                                          "•"))))))

  ;; Set faces for heading levels
  (dolist (face '((org-level-1 . 1.2)
                  (org-level-2 . 1.1)
                  (org-level-3 . 1.05)
                  (org-level-4 . 1.0)
                  (org-level-5 . 1.1)
                  (org-level-6 . 1.1)
                  (org-level-7 . 1.1)
                  (org-level-8 . 1.1)))
    (set-face-attribute (car face) nil :font "Cantarell" :weight 'regular :height (cdr face)))

  ;; Ensure that anything that should be fixed-pitch in Org files appears that way
  (set-face-attribute 'org-block nil    :foreground nil :inherit 'fixed-pitch)
  (set-face-attribute 'org-table nil    :inherit 'fixed-pitch)
  (set-face-attribute 'org-formula nil  :inherit 'fixed-pitch)
  (set-face-attribute 'org-code nil     :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-table nil    :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-checkbox nil  :inherit 'fixed-pitch)
  (set-face-attribute 'line-number nil :inherit 'fixed-pitch)
  (set-face-attribute 'line-number-current-line nil :inherit 'fixed-pitch))


(defun opal/org-mode-setup ()
  (org-indent-mode)
  (variable-pitch-mode 1)
  (visual-line-mode 1)
  (setq org-startup-folded t))

(use-package org
  :straight t
  :commands (org-capture org-agenda)
  :hook (org-mode . opal/org-mode-setup)
  :config
  (setq org-ellipsis " ▾")
  (setq org-agenda-start-with-log-mode t)
  (setq org-log-done 'time)
  (setq org-log-into-drawer t)

  (setq org-agenda-files
        '("~/Org/projects/"
          "~/Org/tasks/"))
  

  (require 'org-habit)
  (add-to-list 'org-modules 'org-habit)
  (setq org-habit-graph-column 60)

  (setq org-todo-keywords
  '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
    (sequence "BACKLOG(b)" "PLAN(p)" "READY(r)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "COMPLETED(c)" "CANC(k@)")))

  (setq org-refile-targets
  '(("archive.org" :maxlevel . 1)
    ("planner.org" :maxlevel . 1)))

  ;; Save Org buffers after refiling!
  (advice-add 'org-refile :after 'org-save-all-org-buffers)

  (setq org-tag-alist
  '((:startgroup)
					; Put mutually exclusive tags here
    (:endgroup)
    ("@errand" . ?E)
    ("@home" . ?H)
    ("@work" . ?W)
    ("agenda" . ?a)
    ("planning" . ?p)
    ("publish" . ?P)
    ("batch" . ?b)
    ("note" . ?n)
    ("idea" . ?i)))

  ;; Configure custom agenda views
  (setq org-agenda-custom-commands
	'(("d" "Dashboard"
	   ((agenda "" ((org-deadline-warning-days 7)))
	    (todo "NEXT"
		  ((org-agenda-overriding-header "Next Tasks")))
	    (tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects")))))

	  ("n" "Next Tasks"
	   ((todo "NEXT"
		  ((org-agenda-overriding-header "Next Tasks")))))

	  ;; Low-effort next actions
	  ("e" tags-todo "+TODO=\"NEXT\"+Effort<15&+Effort>0"
	   ((org-agenda-overriding-header "Low Effort Tasks")
	    (org-agenda-max-todos 20)
	    (org-agenda-files org-agenda-files)))))

  ;; Create capture templates
  (setq org-capture-templates
	`(("t" "Tasks")
	  ("tt" "Task" entry (file+olp "~/org/planner/tasks.org" "Inbox")
	   "* TODO %?\n  %U\n  %a\n  %i" :empty-lines 1)

	  ("p" "Projects")
	  ("pp" "Project File" entry (file+olp "~/org/projects/auto-infra-overview.org" "Inbox")
	   "* TODO %?\n  %U\n  %a\n  %i" :empty-lines 1)))

  ;; Tell Org to stop indenting inside of org source blocks.
  (setq org-edit-src-content-indentation 0)

  ;; Set org agenda dir
  (setq org-directory "~/Org/")

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

  ;; Init org font setup
  (opal/org-font-setup))
#+end_src

*** Pretty Bullets
Creates nicer (-) bullets.
#+begin_src emacs-lisp
(use-package org-bullets
  :straight t
  :after org
  :hook (org-mode . org-bullets-mode)
  :custom
  (org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●")))
#+end_src

*** Org Table of Contents
#+begin_src emacs-lisp
(use-package org-make-toc :straight t)
#+end_src

*** Visual Fill Mode
Creates margins in Org mode to make it more pleasant to look at.
#+begin_src emacs-lisp

(defun opal/org-mode-visual-fill ()
  (setq visual-fill-column-width 100
	visual-fill-column-center-text t)
  (visual-fill-column-mode 1))

(use-package visual-fill-column
  :straight t
  :hook (org-mode . opal/org-mode-visual-fill))

#+end_src

*** Org Babel
#+begin_src emacs-lisp
;; Load languages for babel code blocks.
(with-eval-after-load 'org
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (lisp . t)))

  (push '("conf-unix" . conf-unix) org-src-lang-modes))

;; Set geiser default language
(setq geiser-default-implementation '(guile))

(with-eval-after-load 'org
  (require 'org-tempo)

  (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
  (add-to-list 'org-structure-template-alist '("scm" . "src scheme"))
  (add-to-list 'org-structure-template-alist '("cl" . "src lisp")))
#+end_src
** Development/Editing
*** General
**** Syntax (Flycheck)
#+begin_src emacs-lisp
(use-package flycheck :straight t)
#+end_src
**** Tabs/Spaces
#+begin_src emacs-lisp
(setq-default tab-width 2)                ;; Set tab indent to 2
(setq-default evil-shift-width tab-width) ;; Ensure evil matches tab-width
(setq-default indent-tabs-mode nil)       ;; Use spaces instead of tabs

;; Clean up whitespace
(use-package ws-butler :straight t)
#+end_src
**** Rainbow Delimiters
#+begin_src emacs-lisp
(use-package rainbow-delimiters
  :straight t
  :init
  (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)
  (add-hook 'org-mode-hook #'rainbow-delimiters-mode))
#+end_src
**** Rainbow Mode
Sets HTML strings to have the background of the color they represent.
#+begin_src emacs-lisp
(use-package rainbow-mode :straight t)
#+end_src
*** Magit
#+begin_src emacs-lisp
(use-package magit
  :straight t)
#+end_src
*** Projectile
#+begin_src emacs-lisp
(use-package projectile
  :straight t
  :config
  (projectile-mode)
  :init
  (when (file-directory-p "~/Projects/Code")
    (setq projectile-project-search-path '("~/Projects/Code")))
  (setq projectile-switch-project-action #'projectile-dired))
#+end_src
*** LSP
#+begin_src emacs-lisp
(use-package lsp-mode
  :straight t
  :commands (lsp lsp-deferred)
  :config
  (lsp-enable-which-key-integration t))

(use-package lsp-ui
  :straight t
  :hook (lsp-mode . lsp-ui-mode)
  :custom
  (lsp-ui-doc-position 'bottom))

#+end_src

*** Python
#+begin_src emacs-lisp
(use-package python-mode
  :ensure t
  :hook (python-mode . lsp-deferred))
#+end_src

*** Ruby
**** Code Completion
#+begin_src emacs-lisp
(use-package robe
  :straight t)
#+end_src
**** Bundler
#+begin_src emacs-lisp
(use-package bundler
  :straight t)
#+end_src

*** Lisp
#+begin_src emacs-lisp
(use-package parinfer
  :straight t
  :init
  (progn
    (setq parinfer-extensions
    '(defaults
       pretty-parens
       evil))
  (add-hook 'prog-mode-hook #'parinfer-mode)))
#+end_src  
*** Picolisp
#+begin_src emacs-lisp
(use-package plisp-mode
  :straight t)
#+end_src
*** Common Lisp
#+begin_src emacs-lisp
(defvar inferior-lisp-program "sbcl")
(use-package sly :straight t)
#+end_src 
*** Scheme
#+begin_src emacs-lisp
(use-package geiser
  :straight t
  :ensure t)

(use-package geiser-guile
  :straight t)

#+end_src
*** Snippets
Snippets allow certain files to be filled with a template depending on its filename extension.
#+begin_src emacs-lisp
(use-package yasnippet
  :straight t
  :hook (prog-mode . yas-minor-mode)
  :config
  (yas-reload-all))
#+end_src
** Eshell
#+begin_src emacs-lisp
(use-package eshell
  :straight t
  :config
  ;; Save command history when commands are entered
  (add-hook 'eshell-pre-command-hook 'eshell-save-some-history)
  ;; Truncate buffer for performance
  (add-to-list 'eshell-output-filter-functions 'eshell-truncate-buffer)
  (evil-normalize-keymaps)

  (setq eshell-banner-message
        '(format "%s %s\n"
                 (propertize (format " %s " (string-trim (buffer-name)))
                             'face 'mode-line-highlight)
                 (propertize (current-time-string)
                             'face 'font-lock-keyword-face)))
  
  (setq eshell-scroll-to-bottom-on-input 'all
        eshell-scroll-to-bottom-on-output 'all
        eshell-kill-processes-on-exit t
        eshell-hist-ignoredups t
        eshell-prompt-regexp "^.* λ "
        ;; em-glob
        eshell-history-size         10000
        eshell-buffer-maximum-lines 10000
        eshell-scroll-to-bottom-on-input t)

  (with-eval-after-load 'esh-opt
    (setq eshell-destroy-buffer-when-process-dies t)
    (setq eshell-visual-commands '("htop" "zsh" "vim"))))
  
#+end_src

** Vterm
#+begin_src emacs-lisp
(use-package vterm
  :straight t
  :commands vterm
  :config
  (setq term-prompt-regexp "^[^#$%>\n]*[#$%>] *")  ;; Terminal prompt
  (setq vterm-shell "zsh")
  (setq vterm-max-scrollback 10000))
#+end_src

** PDF
#+begin_src emacs-lisp
(use-package pdf-tools
  :straight t
  :init
  (pdf-tools-install))
#+end_src

** E-Mail
I've    been using IceDove/Thunderbird for e-mail until I get around to making this a comparable solutions.

#+begin_src emacs-lisp
;; (use-package mu4e
;;   :straight t
;;   :config
;;   ;; This is set to 't' to avoid mail syncing issues when using mbsync
;;   (setq mu4e-change-filenames-when-moving t)

;;   ;; Refresh mail using isync every 5 minutes
;;   (setq mu4e-update-interval (* 5 60))
;;   (setq mu4e-get-mail-command "mbsync -a -c ~/Dotfiles/.config/mbsync/mbsyncrc")
;;   (setq mu4e-maildir "~/Mail")

;;   (setq mu4e-contexts
;;         (list
;;          ;; Opal.sh
;;          (make-mu4e-context
;;           :name "Ry P."
;;           :match-func
;;           (lambda (msg)
;;             (when msg
;;               (string-prefix-p "/opal.sh" (mu4e-message-field msg :maildir))))

;;           :vars '((user-mail-address . "ry@opal.sh")
;;                   (user-full-name . "Ry P.")
;;                   (mu4e-drafts-folder . "/opal.sh/Drafts")
;;                   (mu4e-sent-folder . "/opal.sh/Sent")
;;                   (mu4e-trash-folder . "/opal.sh/Trash")))))

;;   (setq mu4e-maildir-shortcuts
;;         '(("/opal.sh/Inbox" . ?i)
;;           ("/opal.sh/Sent"  . ?s)
;;           ("/opal.sh/Trash" . ?t)
;;           ("/opal.sh/Drafts" . ?d))))
#+end_src
** IRC
Right now I'm pretty happy with Matrix bridging into IRC rooms so this will stay commented out for now. I might try out the Emacs Matrix client at some point soon.

#+begin_src emacs-lisp
;; (use-package erc
;;   :straight t
;;   (setq erc-server "irc.libera.chat" ;sets default server
;; 	erc-nick "opalvaults" ; Sets nick
;; 	erc-user-full-name "opalvaults"
;; 	erc-track-shorten-start 8
;; 	erc-kill-buffer-on-part t
;; 	erc-auto-query 'bury
;; 	erc-fill-column 90
;; 	erc-fill-function 'erc-fill-static
;; 	erc-fill-static-center 20
;; 	erc-track-visibility nil
;; 	erc-interpret-mirc-color t
;; 	erc-rename-buffers t
;; 	erc-track-exclude-server-buffer t))
#+end_src
