#+PROPERTY: header-args:emacs-lisp :tangle ~/Dotfiles/.config/emacs/init.el :mkdirp yes :lexical yes #+TITLE: Emacs Configuration Todo: - Yassnippet - Get Email Working again - Get IRC working :PROPERTIES: :VISIBILITY: children :END: * Table of Contents :TOC_4_gh: * 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. ** Bootstraping straight.el #+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) #+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) (setq inhibit-startup-message t) ;; Disable line mode for specific major/minor modes. (dolist (mode '(org-mode-hook)) 'term-mode-hook 'shell-mode-hook 'eshell-mode-hook (add-hook mode (lambda () (display-line-numbers-mode 0)))) #+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))) #+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) :custom (counsel-describe-function-function #'helpful-callable) (counsel-describe-variable-function #'helpful-variable) :bind ([remap describe-function] . counsel-describe-function) ([remap describe-command] . helpful-command) ([remap describe-variable] . counsel-describe-variable) ([remap describe-key] . 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 * Theme I really enjoy [[https://protesilaos.com/emacs/modus-themes][Modus Themes]] by Protesilaos Stavrou. They are minimal, high contrast, and easy on the eyes. #+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 ("" . modus-themes-toggle)) #+end_src * Fonts Mozilla's Fira fonts are pretty, and the Sans and Code types look excellent with the corresponding mode. #+begin_src emacs-lisp (set-face-attribute 'default nil :font "Fira Code" :height 125 :weight 'medium) (set-face-attribute 'variable-pitch nil :font "Fira Sans" :height 1.0 :weight 'regular) (set-face-attribute 'fixed-pitch nil :font "Fira Code" :height 1.0 :weight 'medium) #+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 "") '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) "," #'persp-switch-to-buffer ;; Switch to perspective.el minibuffer "<" #'switch-to-buffer ;; Switch to non-perspective.el minibuffer "s" #'swiper ;; Search in current buffer ;; Magit "gg" #'magit-status "gi" #'magit-init "gc" #'magit-clone ;; Buffers "bd" #'kill-buffer ;; 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 At the moment, I'm trying out Ivy and counsel but I've used Vertico and liked it as well. ** Ivy #+begin_src emacs-lisp (use-package ivy :straight t :diminish :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 ** Counsel #+begin_src emacs-lisp (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 * Org #+begin_src emacs-lisp ;; Set org agenda dir (setq org-directory "~/Org/") ;; Tell Org to stop indenting inside of org source blocks. (setq org-edit-src-content-indentation 0) ;; Open links in browser (setq browse-url-browser-function 'browse-url-generic browse-url-generic-program "firefox") (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))) ;; 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 ** 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))) (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 '("py" . "src python")) (add-to-list 'org-structure-template-alist '("yml" . "src yaml")) (add-to-list 'org-structure-template-alist '("scm" . "src scheme")) (add-to-list 'org-structure-template-alist '("js" . "src javascript")) (add-to-list 'org-structure-template-alist '("html" . "src html")) (add-to-list 'org-structure-template-alist '("css" . "src css")) (add-to-list 'org-structure-template-alist '("cl" . "src lisp"))) #+end_src * Programming/Editor ** General *** Syntax (Flycheck) #+begin_src emacs-lisp (use-package flycheck :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 ** 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 * Magit #+begin_src emacs-lisp (use-package magit :straight t) #+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