Find a file
2022-05-12 12:01:46 -05:00
eln-cache trying to fix 2022-01-03 12:49:32 -06:00
elpa/gnupg trying to fix 2022-01-03 12:49:32 -06:00
eshell fixing eshell diff 2021-03-23 07:01:31 -05:00
yasnippets Switching to vertico, corfu, orderless instead of sel, comp, presc. 2022-03-01 09:47:53 -06:00
.gitignore Not sure what happened here.... O.O 2022-02-25 13:54:32 -06:00
early-init.el making pgtk work better and setting up a bit better ui 2022-05-12 12:01:46 -05:00
init.el making pgtk work better and setting up a bit better ui 2022-05-12 12:01:46 -05:00
README.org making pgtk work better and setting up a bit better ui 2022-05-12 12:01:46 -05:00
recentf making emacs work better under nixos 2022-05-08 06:24:57 -05:00
templates making emacs work better under nixos 2022-05-08 06:24:57 -05:00

Chris's Personal Emacs Config

Init

This init section tangles out to init.el. We'll do most of our configuring here.

Startup Performance

Let's create a message that will tell us how long it takes emacs to load in order to keep an eye on performance.

;;; init.el -*- lexical-binding: t; -*-
(defun chris/display-startup-time ()
  (message "Emacs loaded in %s with %d garbage collections."
           (format "%.2f seconds"
                 (float-time
                (time-subtract after-init-time before-init-time)))
         gcs-done))
(add-hook 'emacs-startup-hook #'chris/display-startup-time)

Let's also set the gc-cons-threshold variable to a high setting for the remainder of our setup process to speed things up. This is no long done here but instead is done in the early-init.el file.

(setq gc-cons-threshold 50000000)

Set basic UI config

Let's start by making some basic ui changes like turning off the scrollbar, toolbar, menu, tooltips, and setting our font and some things.

(setq inhibit-startup-message t)

(scroll-bar-mode -1)
(tool-bar-mode -1)
(tooltip-mode -1)
(set-fringe-mode +1)

(menu-bar-mode -1)
(blink-cursor-mode -1)
(column-number-mode +1)
(setq-default indent-tabs-mode nil)

(setq comp-deferred-compilation-deny-list nil)
(setq frame-resize-pixelwise t)

In order to have this config work on both my desktop with regular joe-schmoe monitors and my laptop with new-hotness HiDPI monitor, I will set the font size if my system is the laptop to much higher.

(if (string-equal (system-name) "syl")
    (defvar chris/default-font-size 120)
  (defvar chris/default-font-size 120))

(defun chris/set-font-faces ()
  "Set the faces for our fonts"
  (message "Setting faces!")
  (set-face-attribute 'default nil :font "VictorMono Nerd Font"
                      :height chris/default-font-size)
  (set-face-attribute 'fixed-pitch nil :font "VictorMono Nerd Font"
                      :height chris/default-font-size)
  (set-face-attribute 'variable-pitch nil :font "Noto Sans"
                      :height (+ chris/default-font-size (/ chris/default-font-size 12))
                      :weight 'regular))

(defun chris/set-transparency ()
  "Set the frame to be transparent on Wayland compositors"
  (set-frame-parameter (selected-frame) 'alpha '(100 . 100))
  (add-to-list 'default-frame-alist '(alpha . (100 . 100)))
  (add-to-list 'initial-frame-alist '(alpha . (100 . 100))))

(if (daemonp)
    (add-hook 'after-make-frame-functions
              (lambda (frame)
                (with-selected-frame frame
                  (chris/set-font-faces)
                  (chris/set-transparency)
                  (tool-bar-mode -1)))
              (chris/set-font-faces)))

Then let's make sure line-numbers are relative and on. And let's turn on visual-line-mode globally.

(setq display-line-numbers-type 'relative)
(global-display-line-numbers-mode +1)
(add-hook 'prog-mode-hook (display-line-numbers-mode +1))
(global-visual-line-mode +1)

I'm adding a terminal to my workflow because sometimes that's better for me.

(defun on-frame-open (frame)
  (if (not (display-graphic-p frame))
    (set-face-background 'default "unspecified-bg" frame)))
(add-hook 'after-make-frame-functions 'on-frame-open)

Here are some ui changes I pulled from Doom Emacs

;; always avoid GUI
(setq use-dialog-box nil)
;; Don't display floating tooltips; display their contents in the echo-area,
;; because native tooltips are ugly.
(when (bound-and-true-p tooltip-mode)
  (tooltip-mode -1))
;; ...especially on linux
(setq x-gtk-use-system-tooltips nil)

 ;; Favor vertical splits over horizontal ones. Screens are usually wide.
(setq split-width-threshold 160
      split-height-threshold nil)

Let's make doc-view better

(setq doc-view-resolution 192)

I need to fix evil-org and these seems about good as place as any to fix it.

(fset 'evil-redirect-digit-argument 'ignore)

Also, real quick let's make sure that <escape> works as the same as <C-g>

(global-set-key (kbd "<escape>") 'keyboard-escape-quit)

Let's also turn on recentf-mode.

(recentf-mode +1)

Finally this is not part of the UI but let's do this early and start the server so I can use emacsclient from my WM.

(server-start)

I will also add my personal scripts to emacs' PATH

(add-to-list 'exec-path "/home/chris/scripts")

Let's also set org-mode as the scratch buffer mode

(setq initial-major-mode 'org-mode)
(setq initial-scratch-message "#+TITLE: SCRATCH\n#+DESCRIPTION: This buffer is for temporary things")

Let's bootstrap straight.el

To use straight we need to bootstrap it. This code is pulled right from Straight's github repo.

(setq straight-fix-org t)
(setq straight-check-for-modifications '(check-on-save find-when-checking))
(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))

(setq straight-use-package-by-default t)

(straight-use-package 'use-package)
(setq use-package-verbose t)
(defalias 'yes-or-no-p 'y-or-n-p)

Now let's make sure our package archives includes the newer org.

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

Command-log-mode

  (use-package command-log-mode
    :commands command-log-mode)

All the icons is super pretty and needed for doom-modeline.

(use-package all-the-icons)

Probably the prettiest and best modeline I've found.

(use-package doom-modeline
  :ensure t
  :init
  (doom-modeline-mode 1)
  (setq doom-modeline-height 35
        doom-modeline-bar-width 3
        all-the-icons-scale-factor 0.9)
  (if (daemonp)
      (add-hook 'after-make-frame-functions
                (lambda (frame)
                  (with-selected-frame frame
                    (setq doom-modeline-icon t))))))

Again, doom is pretty and I have fallen in love with the snazzy theme and use it about anywhere I can.

(use-package doom-themes
  :ensure t
  :init (load-theme 'doom-snazzy t))

Let's make parens and other delimiters easier to tell apart by making nested ones different colors.

(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode))
(use-package smartparens
  :defer 1
  :config
  (smartparens-global-mode +1))
(use-package aggressive-indent
  :defer 1
  :config
  (aggressive-indent-mode -1))
(use-package adaptive-wrap
  :defer t)
(use-package which-key
  :config
  (setq which-key-idle-delay 0.3)
  (which-key-mode)
  :defer 1)

Fix NixOS

I am currently using NixOS. In order for emacs to have access to certain programs, we need to set some environment variables

(add-to-list 'exec-path "/home/chris/.nix-profile/bin")
(add-to-list 'exec-path "/etc/profiles/per-user/chris/bin")
(add-to-list 'exec-path "/run/current-system/sw/bin")
(add-to-list 'exec-path "/opt/android-sdk/cmdline-tools/latest/bin")

(setq exec-directory "/run/current-system/sw/bin")
(setenv "NIX_CONF_DIR" "/etc/nix")
(setenv "NIX_OTHER_STORES"  "/run/nix/remote-stores/*/nix")
(setenv "NIX_PATH" "nixpkgs=%h/nixpkgs:nixos=%h/nixpkgs/nixos:nixos-config=/etc/nixos/configuration.nix")
(setenv "NIX_PROFILES" "${pkgs.lib.concatStringsSep " " config.environment.profiles}")
(setenv "NIX_REMOTE" "daemon")
(setenv "NIX_USER_PROFILE_DIR" "/nix/var/nix/profiles/per-user/%u")
(load "site-paths" t)
(use-package exec-path-from-shell
  :demand
  :commands exec-path-from-shell-initialize
  :custom
  (exec-path-from-shell-arguments '("-l"))
  :config
  (exec-path-from-shell-initialize))

Spell Check

(executable-find "ssh")
(setq ispell-program-name "hunspell"
      ispell-local-dictionary "en_US"
      ispell-local-dictionary-alist
      ;; Please note the list `("-d" "en_US")` contains ACTUAL parameters passed to hunspell
      ;; You could use `("-d" "en_US,en_US-med")` to check with multiple dictionaries
      '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)))

Keep Folders Clean

Let's use no-littering in order to stop emacs from filling all our folders with junk.

(use-package no-littering)

;; no-littering doesn't set this by default so we must place
;; auto save files in the same path as it uses for sessions
(setq auto-save-file-name-transforms
      `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))

Ligatures

Here let's try to add ligatures to our font system since the VictorMono Nerd Font supports all ligatures being a "Nerd Font".

(let ((alist '((?! . "\\(?:!\\(?:==\\|[!=]\\)\\)")
               (?# . "\\(?:#\\(?:###?\\|_(\\|[!#(:=?[_{]\\)\\)")
               (?$ . "\\(?:\\$>\\)")
               (?& . "\\(?:&&&?\\)")
               (?* . "\\(?:\\*\\(?:\\*\\*\\|[/>]\\)\\)")
               (?+ . "\\(?:\\+\\(?:\\+\\+\\|[+>]\\)\\)")
               (?- . "\\(?:-\\(?:-[>-]\\|<<\\|>>\\|[<>|~-]\\)\\)")
               (?. . "\\(?:\\.\\(?:\\.[.<]\\|[.=?-]\\)\\)")
               (?/ . "\\(?:/\\(?:\\*\\*\\|//\\|==\\|[*/=>]\\)\\)")
               (?: . "\\(?::\\(?:::\\|\\?>\\|[:<-?]\\)\\)")
               (?\; . "\\(?:;;\\)")
               (?< . "\\(?:<\\(?:!--\\|\\$>\\|\\*>\\|\\+>\\|-[<>|]\\|/>\\|<[<=-]\\|=\\(?:=>\\|[<=>|]\\)\\||\\(?:||::=\\|[>|]\\)\\|~[>~]\\|[$*+/:<=>|~-]\\)\\)")
               (?= . "\\(?:=\\(?:!=\\|/=\\|:=\\|=[=>]\\|>>\\|[=>]\\)\\)")
               (?> . "\\(?:>\\(?:=>\\|>[=>-]\\|[]:=-]\\)\\)")
               (?? . "\\(?:\\?[.:=?]\\)")
               (?\[ . "\\(?:\\[\\(?:||]\\|[<|]\\)\\)")
               (?\ . "\\(?:\\\\/?\\)")
               (?\] . "\\(?:]#\\)")
               (?^ . "\\(?:\\^=\\)")
               (?_ . "\\(?:_\\(?:|?_\\)\\)")
               (?{ . "\\(?:{|\\)")
               (?| . "\\(?:|\\(?:->\\|=>\\||\\(?:|>\\|[=>-]\\)\\|[]=>|}-]\\)\\)")
               (?~ . "\\(?:~\\(?:~>\\|[=>@~-]\\)\\)"))))
  (dolist (char-regexp alist)
    (set-char-table-range composition-function-table (car char-regexp)
                          `([,(cdr char-regexp) 0 font-shape-gstring]))))

Keybindings

There are two major packages we need to get the functionality I desire. Evil and general.

(use-package evil
  :init
  (setq evil-want-integration t
        evil-want-keybinding nil
        evil-want-C-i-jump nil
        evil-want-C-u-scroll t
        evil-respect-visual-line-mode t
        evil-want-C-u-delete t
        evil-undo-system 'undo-redo
        scroll-conservatively 101
        hscroll-margin 2
        scroll-margin 0
        scroll-preserve-screen-position t
        hscroll-step 1
        evil-vsplit-window-right t)
  :config
  (evil-mode +1))

This evil-collection package includes a lot of other evil based things.

(use-package evil-collection
  :after evil
  :config (evil-collection-init))
(use-package general
  :init
  (general-evil-setup)
  :config
  (defun chris/edit-emacs-config ()
    "open the emacs config to edit"
    (interactive)
    (find-file (expand-file-name "README.org" user-emacs-directory)))
  (defun chris/open-bible ()
    "find a bible to open"
    (interactive)
    (find-file "~/org/bibles/"))
  (general-create-definer chris/leader-keys
    :keymaps '(normal visual emacs)
    :prefix "SPC")
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "b" '(:ignore t :which-key "buffer")
    "t" '(:ignore t :which-key "toggle")
    "f" '(:ignore t :which-key "file")
    "w" '(:ignore t :which-key "window")
    "s" '(:ignore t :which-key "search")
    "i" '(:ignore t :which-key "insert")
    "o" '(:ignore t :which-key "open")
    "oa" '(:ignore t :which-key "org agenda")
    "of" '(:ignore t :which-key "elfeed")
    "h" '(:ignore t :which-key "help")
    "n" '(:ignore t :which-key "notes")
    "m" '(:ignore t :which-key "music")
    "l" '(:ignore t :which-key "lsp")
    "sp" '(:ignore t :which-key "passwords")
    "bs" '(consult-buffer :which-key "buffer search")
    "bd" '(kill-this-buffer :which-key "kill buffer")
    "bi" '(ibuffer :which-key "ibuffer")
    "tt" '(consult-theme :which-key "choose theme")
    "tl" '(toggle-truncate-lines :which-key "truncate lines")
    "ff" '(find-file :which-key "find file")
    "fb" '(chris/open-bible :which-key "find bible book")
    "fr" '(consult-recent-file :which-key "recent file")
    "fs" '(save-buffer :which-key "save")
    "fE" '(consult-file-externally :which-key "find file externally")
    "fe" '(chris/edit-emacs-config :which-key "open config")
    "hf" '(helpful-callable :which-key "describe-function")
    "hv" '(helpful-variable :which-key "describe-variable")
    "hk" '(helpful-key :which-key "describe-key")
    "hF" '(describe-face :which-key "describe-face")
    "hb" '(general-describe-keybindings :which-key "describe-bindings")
    "hi" '(info :which-key "info manual")
    "ht" '(which-key-show-top-level :which-key "show top-level keybindings")
    "ss" '(consult-line :which-key "consult search")
    "sr" '(consult-ripgrep :which-key "consult ripgrep")
    "oP" '(proced :which-key "proced")
    "wo" '(other-window :which-key "other window")
    "wd" '(delete-window :which-key "other window")
    "wv" '(evil-window-vsplit :which-key "split window vertically")
    "ws" '(evil-window-split :which-key "split window horizontally")
    "wj" '(evil-window-down :which-key "down window")
    "wk" '(evil-window-up :which-key "up window")
    "wh" '(evil-window-left :which-key "left window")
    "wl" '(evil-window-right :which-key "right window")
    ";" '(execute-extended-command :which-key "execute command")
    ":" '(eval-expression :which-key "evaluate expression"))
  (general-def 'minibuffer-local-map
    "C-v" 'evil-paste-after)
  (general-def 'normal
    "gcc" 'comment-line
    "K" 'helpful-at-point
    "C-S-l" 'evil-window-increase-width
    "C-S-j" 'evil-window-decrease-height
    "C-S-k" 'evil-window-increase-height
    "C-S-h" 'evil-window-decrease-width)
  (general-def 'normal Info-mode-map
    "RET" 'Info-follow-nearest-node
    "p" 'Info-prev
    "n" 'Info-next))
(use-package evil-escape
  :after evil
  :init (evil-escape-mode +1)
  :config
  (setq evil-escape-key-sequence "fd"
        evil-escape-delay 0.3))
(use-package evil-surround
  :after evil
  :config
  (global-evil-surround-mode +1))

Org Mode

Org-Mode needs to be loaded pretty high in the file so that we are ensuring things get picked up in the correct order. This has been a problem for me in the past so I prefer it right after setting some keybindings and then much later loading things like roam super-agenda and others.

Let's start by creating a self contained function of what I'd like started on every org buffer.

(defun chris/org-mode-setup ()
  (interactive)
  (org-indent-mode +1)
  (toc-org-mode +1)
  (visual-fill-column-mode +1)
  (display-line-numbers-mode -1)
  (variable-pitch-mode +1)
  (flyspell-mode +1)
  (setq visual-fill-column-width 100
        visual-fill-column-center-text t)

  ;;Let's make sure org-mode faces are inheriting fixed pitch faces.
  (dolist (face '(org-block
                  org-block-begin-line
                  org-block-end-line
                  org-code
                  org-document-info-keyword
                  org-meta-line
                  org-table
                  org-date
                  org-verbatim))
    (set-face-attribute `,face nil :inherit 'fixed-pitch))

  (set-face-attribute 'org-block-end-line nil :inherit 'org-block-begin-line)
  (set-face-attribute 'org-quote nil :background "#242631" :inherit 'fixed-pitch))

(defun chris/org-agenda-setup ()
  (interactive)
  (org-indent-mode +1)
  (toc-org-mode +1)
  (visual-fill-column-mode +1)
  (display-line-numbers-mode -1)
  (variable-pitch-mode -1)
  (toggle-truncate-lines +1)
  (evil-normal-state)
  (setq visual-fill-column-width 120
        visual-fill-column-center-text t))

This is the use-package definition with a lot of customization. Need to setup auto tangle and make sure I have some structure templates for org-mode.

Part of this config includes some special capture templates for my work as a youth minister. I create lessons through both org-mode and org-roam capture templates. The first part comes from org-roam, then the next is org-mode.

(use-package org
  :straight t
  :defer 1
  :config
  (setq org-startup-indented t
        org-edit-src-content-indentation 0
        org-agenda-sticky t
        org-fontify-quote-and-verse-blocks t
        org-src-preserve-indentation t
        org-src-window-setup 'other-window
        org-agenda-current-time-string "⭠ now ────────────────")

  (add-hook 'org-mode-hook 'chris/org-mode-setup)

  (org-babel-do-load-languages 'org-babel-load-languages
                               '((emacs-lisp . t)
                                 (python . t)
                                 (ditaa . t)
                                 (shell . t)))

  (setq org-ditaa-jar-path "/usr/bin/ditaa")

  (require 'org-tempo)
  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
  (add-to-list 'org-structure-template-alist '("cl" . "src common-lisp"))
  (add-to-list 'org-structure-template-alist '("py" . "src python"))
  (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
  (add-to-list 'org-structure-template-alist '("yaml" . "src yaml"))
  (add-to-list 'org-structure-template-alist '("yml" . "src yaml"))
  (add-to-list 'org-structure-template-alist '("q" . "quote"))
  (add-to-list 'org-structure-template-alist '("rc" . "src restclient"))

  (setq org-capture-templates
        '(("t" "Personal todo" entry
           (file "todo/todo.org")
           (file ".templates/tasks.org") :prepend t)
          ("n" "Personal notes" entry
           (file "todo/notes.org")
           "* %u %?\n%i\n%a" :prepend t)
          ("j" "Journal" entry
           (file+olp+datetree +org-capture-journal-file)
           "* %U %?\n%i\n%a" :prepend t)
          ("p" "TFC Plan" entry
           (function chris/org-roam-capture-lesson-file)
           (file ".templates/tfcplantemplate.org")
           :prepend nil
           :jump-to-captured t
           :empty-lines 1)
          ("P" "TFC Posts" entry
           (file+headline "nvtfc_social_media.org" "Posts")
           (file ".templates/posts.org")
           :prepend t
           :jump-to-captured t)
          ("r" "Templates for projects")
          ("rt" "Project-local todo" entry
           (file+headline chris/project-todo "Inbox")
           "* TODO %?\n%a\n%i\n\n" :prepend t)
          ("rn" "Project-local notes" entry
           (file+headline chris/project-todo "Notes")
           "* %U %?\n%a\n%i\n\n" :prepend t)
          ("rc" "Project-local changelog" entry
           (file+headline chris/project-changelog "Changelog")
           "* %U %?\n%a\n%i\n\n" :prepend t))
        org-capture-use-agenda-date t
        org-agenda-timegrid-use-ampm t
        org-agenda-show-inherited-tags nil
        org-agenda-tags-column -100)

  (setq org-agenda-prefix-format '((agenda . " %i %?-12t% s")
                                   (todo . " %i %-12:c")
                                   (tags . " %i %-12:c")
                                   (search . " %i %-12:c")))

  (setq org-agenda-category-icon-alist
        '(("todo" "~/org/icons/task.png" nil nil :ascent center)
          ("lesson" "~/org/icons/book.png" nil nil :ascent center)
          ("dev" "~/org/icons/coding.png" nil nil :ascent center)
          ("TODO" "~/org/icons/coding.png" nil nil :ascent center)))

  (setq org-imenu-depth 4
        org-odt-styles-file "/home/chris/org/style.odt"
        org-export-with-toc nil
        org-export-with-author nil
        org-todo-keywords
        '((sequence "TODO(t)" "PROJ(p)" "STRT(s)" "WAIT(w)" "HOLD(h)" "|" "DONE(d)" "CNCL(c)")
          (sequence "[ ](T)" "[-](S)" "[?](W)" "|" "[X](D)"))
        org-agenda-files
        '("/home/chris/org/todo/inbox.org"
          "/home/chris/org/todo/notes.org"
          "/home/chris/org/repetition.org"
          "/home/chris/org/tfc_plans.org"
          "/home/chris/org/ministry_team.org"
          "/home/chris/org/todo/todo.org"
          "/home/chris/org/newsletter.org"
          "/home/chris/org/archive.org"
          "/home/chris/org/nvtfc_social_media.org"
          "/home/chris/dev/church-presenter/TODO.org"
          "/home/chris/org/lessons/")
        org-id-method 'ts
        org-agenda-tags-column -75)

  (defun chris/org-columns-view ()
    "Turn on org-columns overlay and turn off olivetti-mode"
    (interactive)
    (goto-char (point-min))
    (org-content)
    (org-columns)
    (setq visual-fill-column-width 150))

  (defun chris/org-columns-quit ()
    "Remove the org-columns overlay and turn on olivetti-mode"
    (interactive)
    (org-columns-quit)
    (chris/org-mode-setup)
    (setq visual-fill-column-width 100)
    (setq visual-fill-column-width 100))

  ;; (add-hook 'org-agenda-finalize-hook 'evil-normal-state)
  (add-hook 'org-agenda-finalize-hook 'chris/org-agenda-setup)

  (advice-add 'org-agenda-todo :after #'org-save-all-org-buffers)

  (setq org-refile-targets '((org-agenda-files . (:maxlevel . 6))))

  (setq org-agenda-window-setup 'current-window)

  (defun chris/org-agenda ()
    "create a window that houses my org-agenda"
    (interactive)
    (with-selected-frame (make-frame
                          '((name . "org-agenda")
                            (width . 100)))
      (org-agenda-list)))

  (setq org-latex-packages-alist '(("margin=2cm" "geometry" nil)))

  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "c" 'org-capture
    "rr" 'org-refile
    "e" 'org-export-dispatch
    "oa" 'org-agenda-list
    "gt" 'org-babel-tangle)
  ('normal org-agenda-mode-map
           "q" 'org-agenda-quit
           "r" 'org-agenda-redo
           "d" 'org-agenda-deadline
           "s" 'org-agenda-schedule
           "t" 'org-agenda-todo
           "c" 'org-agenda-capture)
  ('normal org-columns-map
           "j" 'outline-next-heading
           "h" 'outline-previous-heading
           "q" 'chris/org-columns-quit)
  ('normal org-mode-map
           "RET" 'chris/org-dwim-at-point
           "gC" 'chris/org-columns-view
           "ge" 'org-edit-src-code
           "gr" 'revert-buffer
           "ze" 'org-emphasize
           "S" 'org-schedule
           "t" 'org-todo)
  ('insert org-mode-map
           "C-i" 'completion-at-point)
  ('normal 'org-src-mode-map
           "q" 'org-edit-src-abort))

We need to create a lesson capture function to find our lesson files differently each time we run our TFC plan capture. This is the most unique part of my capture template. This function uses org-roam-find-file to pick the lesson file that I need to add my lesson plan to. This way the lesson itself is created before the plan.

(defun chris/org-roam-capture-lesson-file ()
  "Function to return the lesson file that is needed for TFC plan capture and move to correct position for plan insertion"
  (interactive)
  ;; (unless org-roam-mode (org-roam-mode +1))
  (let ((node (org-roam-node-read)))
    (org-roam-node-visit node)
    (goto-char (point-min))
    (search-forward "PLAN")))
(defun chris/project-todo ()
  (concat (projectile-project-root) "TODO.org"))
(defun chris/project-changelog ()
  (concat (projectile-project-root) "CHANGELOG.org"))

We are also going to make our config auto-tangle. This is so helpful on saving the .org file tangles out the config automatically.

(defun chris/org-babel-tangle-config ()
  (when (string-equal (buffer-file-name)
                      (expand-file-name "README.org" user-emacs-directory))
    (let ((org-confirm-babel-evaluate nil))
      (org-babel-tangle))))

(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'chris/org-babel-tangle-config
                                              :append :local)))

We also need to add evil-org to make better keybindings.

(use-package evil-org
  :ensure t
  :after org
  :hook (org-mode . (lambda () evil-org-mode))
  :config
  ;; (add-to-list 'evil-digit-bound-motions 'evil-org-beginning-of-line)
  ;; (evil-define-key 'motion 'evil-org-mode
  ;;   (kbd "0") 'evil-org-beginning-of-line)
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys))

Org-Super-Agenda

Super Agenda gives me a really nice way of making the agenda view look a lot better with some better information included.

(use-package org-super-agenda
  :after org
  :init
  (setq org-super-agenda-groups '((:name "📅 Today"
                                   :time-grid t
                                   :scheduled today)
                                  (:name "Due Today"
                                   :deadline today)
                                  (:name "Important"
                                   :priority "A")
                                  (:name "Development"
                                   :category "TODO"
                                   :category "dev")
                                  (:name "Overdue"
                                   :time-grid t
                                   :scheduled past
                                   :deadline past)
                                  (:name "Due soon"
                                   :deadline future)))
  :config
  (org-super-agenda-mode +1)
  (setq org-super-agenda-header-map nil))

Org-Roam

Here we are going to add org-roam. This is a note-takers paradise by adding an automatic backlinking function.

Basic Org-Roam setup. We select the directory and the basic width of the Org-Roam buffer so that it doesn't take too much space on my laptop. We also want to exclude certain files from Org-Roam. All files are synced between machines using syncthing and kept in a version history. I'd like to exclude the version history from Org-Roam using org-roam-file-exclude-regexp.

We also need to setup some capture templates to use some specific setups with my journalling. These include a space for my Homework For Life, tasks for the day, and how I can love on my family.

(use-package org-roam
  :after org
  :ensure t
  :init
  (setq org-roam-v2-ack t)
  :config
  (setq org-roam-directory "~/org"
        org-roam-buffer-width 0.25
        org-roam-file-exclude-regexp ".stversion.*\|.stfolder.*\|.*~.*\|.*sync.*"
        org-roam-db-location "~/.dotemacs/org-roam.db"
        org-roam-completion-everywhere t
        org-roam-capture-templates
        '(("d" "default" plain "%?"
           :if-new (file+head "${slug}.org"
                               "#+TITLE: ${title}\n#+AUTHOR: Chris Cochrun\n#+CREATED: %<%D - %I:%M %p>\n\nj ")
           :unnarrowed t)
          ("b" "bible" plain "%?"
           :if-new (file+head "${slug}.org"
                              "#+TITLE: ${title}\n#+AUTHOR: Chris Cochrun\n#+CREATED: %<%D - %I:%M %p>\n- tags %^G\n\n* %?")
           :unnarrowed t)
          ("l" "TFC Lesson" plain (file ".templates/lessontemplate.org")
           :if-new (file+head "lessons/${slug}.org"
                               "#+TITLE: ${title}\n#+AUTHOR: Chris Cochrun\n#+CREATED: %<%D - %I:%M %p>\n")
           :unnarrowed t))
        org-roam-dailies-capture-templates
        '(("d" "daily" plain #'org-roam-capture--get-point ""
           :immediate-finish t
           :file-name "%<%Y-%m-%d>"
           :head "#+TITLE: %<%Y-%m-%d>\n#+AUTHOR: Chris Cochrun\n#+CREATED: %<%D - %I:%M %p>\n\n* HFL\n* Tasks\n* Family\n** How Do I Love Abbie?")
          ("b" "biblical daily" plain #'org-roam-capture--get-point ""
           :immediate-finish t
           :file-name "%<%Y-%m-%d>-bib"
           :head "#+TITLE: %<%Y-%m-%d> - Biblical\n#+AUTHOR: Chris Cochrun")))
  (org-roam-setup)
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "nf" '(org-roam-node-find :which-key "org roam ff")
    "nr" 'org-roam-buffer-toggle
    "ni" 'org-roam-node-insert
    "nc" 'org-roam-capture
    "njt" 'org-roam-dailies-capture-today
    "ng" 'org-roam-graph))

Org-Roam server. This let's me visualize my notes. In order to use it, I need to go to http://localhost:8080

(use-package websocket)
(use-package org-roam-ui
  :straight (:host github :repo "org-roam/org-roam-ui" :files ("*.el" "out"))
  :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))

Org-Superstar

Org-Superstar makes the stars at the beginning of the line in org-mode a lot prettier.

(use-package org-superstar
  :after org
  :config
  (org-superstar-mode +1)
  (setq org-superstar-headline-bullets-list '("\u25c9" "\u25c8" "" "\u25ce" "\u272c" "\u25c7" "\u2749" "\u2719" "\u2756"))
  (setq org-superstar-item-bullet-alist '((?- . ?\u25b8)
                                          (?+ . ?\u2749)
                                          (?* . ?\u25c9)))
  (set-face-attribute 'org-superstar-item nil :inherit 'org-level-3)
  (add-hook 'org-mode-hook '(org-superstar-mode -1)))

Org Modern

(use-package org-modern
  :straight (:host github :repo "minad/org-modern")
  :config
  
  (custom-set-faces
   '(org-modern-tag ((t :background "#9aedfe" :foreground "#282a36")))
   )
  (global-org-modern-mode +1)
  )

Org DWIM

I've stolen most of this code from Doom Emacs but we want to create a dwim-at-point function for Org-Mode that will fit under the RET key. This allows us to do a bunch of different functions depending on the context of point.

(defun chris/org-dwim-at-point (&optional arg)
  "Do-what-I-mean at point.

If on a:
- checkbox list item or todo heading: toggle it.
- clock: update its time.
- headline: cycle ARCHIVE subtrees, toggle latex fragments and inline images in
  subtree; update statistics cookies/checkboxes and ToCs.
- footnote reference: jump to the footnote's definition
- footnote definition: jump to the first reference of this footnote
- table-row or a TBLFM: recalculate the table's formulas
- table-cell: clear it and go into insert mode. If this is a formula cell,
  recaluclate it instead.
- babel-call: execute the source block
- statistics-cookie: update it.
- latex fragment: toggle it.
- link: follow it
- otherwise, refresh all inline images in current tree."
  (interactive "P")
  (let* ((context (org-element-context))
         (type (org-element-type context)))
    ;; skip over unimportant contexts
    (while (and context (memq type '(verbatim code bold italic underline strike-through subscript superscript)))
      (setq context (org-element-property :parent context)
            type (org-element-type context)))
    (pcase type
      (`headline
       (cond ((memq (bound-and-true-p org-goto-map)
                    (current-active-maps))
              (org-goto-ret))
             ((and (fboundp 'toc-org-insert-toc)
                   (member "TOC" (org-get-tags)))
              (toc-org-insert-toc)
              (message "Updating table of contents"))
             ((string= "ARCHIVE" (car-safe (org-get-tags)))
              (org-force-cycle-archived))
             ((or (org-element-property :todo-type context)
                  (org-element-property :scheduled context))
              (org-todo
               (if (eq (org-element-property :todo-type context) 'done)
                   (or (car (chris/org-get-todo-keywords-for (org-element-property :todo-keyword context)))
                       'todo)
                 'done))))
       ;; Update any metadata or inline previews in this subtree
       (org-update-checkbox-count)
       (org-update-parent-todo-statistics)
       (when (and (fboundp 'toc-org-insert-toc)
                  (member "TOC" (org-get-tags)))
         (toc-org-insert-toc)
         (message "Updating table of contents"))
       (let* ((beg (if (org-before-first-heading-p)
                       (line-beginning-position)
                     (save-excursion (org-back-to-heading) (point))))
              (end (if (org-before-first-heading-p)
                       (line-end-position)
                     (save-excursion (org-end-of-subtree) (point))))
              (overlays (ignore-errors (overlays-in beg end)))
              (latex-overlays
               (cl-find-if (lambda (o) (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay))
                           overlays))
              (image-overlays
               (cl-find-if (lambda (o) (overlay-get o 'org-image-overlay))
                           overlays)))
         (chris/org--toggle-inline-images-in-subtree beg end)
         (if (or image-overlays latex-overlays)
             (org-clear-latex-preview beg end)
           (org--latex-preview-region beg end))))

      (`clock (org-clock-update-time-maybe))

      (`footnote-reference
       (org-footnote-goto-definition (org-element-property :label context)))

      (`footnote-definition
       (org-footnote-goto-previous-reference (org-element-property :label context)))

      ((or `planning `timestamp)
       (org-follow-timestamp-link))

      ((or `table `table-row)
       (if (org-at-TBLFM-p)
           (org-table-calc-current-TBLFM)
         (ignore-errors
           (save-excursion
             (goto-char (org-element-property :contents-begin context))
             (org-call-with-arg 'org-table-recalculate (or arg t))))))

      (`table-cell
       (org-table-blank-field)
       (org-table-recalculate arg)
       (when (and (string-empty-p (string-trim (org-table-get-field)))
                  (bound-and-true-p evil-local-mode))
         (evil-change-state 'insert)))

      (`babel-call
       (org-babel-lob-execute-maybe))

      (`statistics-cookie
       (save-excursion (org-update-statistics-cookies arg)))

      ((or `src-block `inline-src-block)
       (org-babel-execute-src-block))

      ((or `latex-fragment `latex-environment)
       (org-latex-preview))

      (`link
       (let* ((lineage (org-element-lineage context '(link) t))
              (path (org-element-property :path lineage)))
         (if (or (equal (org-element-property :type lineage) "img")
                 (and path (image-type-from-file-name path)))
             (chris/org--toggle-inline-images-in-subtree
              (org-element-property :begin lineage)
              (org-element-property :end lineage))
           (org-open-at-point arg))))

      (`paragraph
       (let ((match (and (org-at-item-checkbox-p) (match-string 1))))
         (org-toggle-checkbox)))

      (_
       (if (or (org-in-regexp org-ts-regexp-both nil t)
               (org-in-regexp org-tsr-regexp-both nil  t)
               (org-in-regexp org-link-any-re nil t))
           (call-interactively #'org-open-at-point)
         (chris/org--toggle-inline-images-in-subtree
          (org-element-property :begin context)
          (org-element-property :end context)))))))


(defun chris/org-get-todo-keywords-for (&optional keyword)
  "Returns the list of todo keywords that KEYWORD belongs to."
  (when keyword
    (cl-loop for (type . keyword-spec)
             in (cl-remove-if-not #'listp org-todo-keywords)
             for keywords =
             (mapcar (lambda (x) (if (string-match "^\\([^(]+\\)(" x)
                                     (match-string 1 x)
                                   x))
                     keyword-spec)
             if (eq type 'sequence)
             if (member keyword keywords)
             return keywords)))

(defun chris/org--toggle-inline-images-in-subtree (&optional beg end refresh)
  "Refresh inline image previews in the current heading/tree."
  (let ((beg (or beg
                 (if (org-before-first-heading-p)
                     (line-beginning-position)
                   (save-excursion (org-back-to-heading) (point)))))
        (end (or end
                 (if (org-before-first-heading-p)
                     (line-end-position)
                   (save-excursion (org-end-of-subtree) (point)))))
        (overlays (cl-remove-if-not (lambda (ov) (overlay-get ov 'org-image-overlay))
                                    (ignore-errors (overlays-in beg end)))))
    (dolist (ov overlays nil)
      (delete-overlay ov)
      (setq org-inline-image-overlays (delete ov org-inline-image-overlays)))
    (when (or refresh (not overlays))
      (org-display-inline-images t t beg end)
      t)))

Emoji

In order to render color emojis I'll use unicode-fonts.

(use-package unicode-fonts
  :ensure t
  :config
  (unicode-fonts-setup))

Or maybe I'll use emojify.

(use-package emojify
  :ensure t
  :hook (after-init . global-emojify-mode)
  :config
  (setq emojify-display-style 'image)
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "ie" '(emojify-insert-emoji :which-key "insert emoji")))

Undo-Tree

I no longer use this since I primarily use Emacs 28 across my machines and have used Emacs's built in undo-redo.

(use-package undo-tree
  :after evil
  :config
  (global-undo-tree-mode +1)
  (setq evil-undo-system 'undo-tree)
  :general
  (general-def 'normal undo-tree-visualizer-mode-map
   "j" 'undo-tree-visualize-redo
   "k" 'undo-tree-visualize-undo))

Undo-Fu

The same applies here as did undo-tree I no longer use this since I primarily use Emacs 28 across my machines and have used Emacs's built in undo-redo.

(use-package undo-fu
  :after evil
  :config
  (setq evil-undo-system 'undo-fu))

Better UI

Olivetti

(use-package olivetti
  :config
  (setq olivetti-body-width 0.6
  olivetti-minimum-body-width 100))

Revert all buffers

(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)

Visual-Fill-Line-Mode

Visual fill column does a lot of the same as olivetti, but works with visual line mode better.

(use-package visual-fill-column
  :after org
  :config
  (setq visual-fill-column-width 100
        visual-fill-column-center-text t))

TOC-ORG

(use-package toc-org
  :after org)

Lin and Pulsar

These two packages created by Prot are interesting to me and may help to make sure I do not loose my place in emacs so much.

(use-package lin
  :config
  (setq lin-mode-hooks
        '(bongo-mode-hook
          dired-mode-hook
          elfeed-search-mode-hook
          git-rebase-mode-hook
          ibuffer-mode-hook
          ilist-mode-hook
          ledger-report-mode-hook
          log-view-mode-hook
          magit-log-mode-hook
          mu4e-headers-mode
          notmuch-search-mode-hook
          notmuch-tree-mode-hook
          occur-mode-hook
          org-agenda-mode-hook
          tabulated-list-mode-hook)))
(use-package pulsar
  :config
  (setq pulsar-pulse-functions
      ;; NOTE 2022-04-09: The commented out functions are from before
      ;; the introduction of `pulsar-pulse-on-window-change'.  Try that
      ;; instead.
      '(recenter-top-bottom
        move-to-window-line-top-bottom
        reposition-window
        ;; bookmark-jump
        ;; other-window
        ;; delete-window
        ;; delete-other-windows
        forward-page
        backward-page
        scroll-up-command
        scroll-down-command
        ;; windmove-right
        ;; windmove-left
        ;; windmove-up
        ;; windmove-down
        ;; windmove-swap-states-right
        ;; windmove-swap-states-left
        ;; windmove-swap-states-up
        ;; windmove-swap-states-down
        ;; tab-new
        ;; tab-close
        ;; tab-next
        org-next-visible-heading
        org-previous-visible-heading
        org-forward-heading-same-level
        org-backward-heading-same-level
        outline-backward-same-level
        outline-forward-same-level
        outline-next-visible-heading
        outline-previous-visible-heading
        outline-up-heading))

  (setq pulsar-pulse-on-window-change t)
  (setq pulsar-pulse t)
  (setq pulsar-delay 0.055)
  (setq pulsar-iterations 10)
  (setq pulsar-face 'ffap)
  (setq pulsar-highlight-face 'pulsar-yellow)

  (pulsar-global-mode 1)
  ;; integration with the `consult' package:
  (add-hook 'consult-after-jump-hook #'pulsar-recenter-top)
  (add-hook 'consult-after-jump-hook #'pulsar-reveal-entry)
  
  ;; integration with the built-in `imenu':
  (add-hook 'imenu-after-jump-hook #'pulsar-recenter-top)
  (add-hook 'imenu-after-jump-hook #'pulsar-reveal-entry))

EWW

Builtin webbrowser for emacs. Trying it out as a text only browser for things.

Completion

My completion framework is a combination of packages so that everything remains seperate and lightweight.

SELECTRUM

I prefer selectrum over Ivy or Helm for completions. It is using the basic completing read system and therefore it is more inline with basic emacs. Also, let's add prescient to be able to filter selectrum well. We'll add some keybindings too for easier navigation on the home row.

(use-package selectrum
  :init
  (selectrum-mode +1)
  :config
  (setq selectrum-max-window-height 8)
  (add-hook 'selectrum-mode-hook 'selectrum-exhibit)

  ;; We need to fix selectrums minibuffer handling for Emacs 28
  (defun selectrum--set-window-height (window &optional height)
  "Set window height of WINDOW to HEIGHT pixel.
If HEIGHT is not given WINDOW will be updated to fit its content
vertically."
  (let* ((lines (length
                 (split-string
                  (overlay-get selectrum--candidates-overlay 'after-string)
                  "\n" t)))
         (dheight (or height
                      (* lines selectrum--line-height)))
         (wheight (window-pixel-height window))
         (window-resize-pixelwise t))
    (window-resize
     window (- dheight wheight) nil nil 'pixelwise)))

  
  :general
  ('selectrum-minibuffer-map
   "C-j" 'selectrum-next-candidate
   "C-k" 'selectrum-previous-candidate
   "C-S-j" 'selectrum-goto-end
   "C-S-k" 'selectrum-goto-beginning
   "TAB" 'selectrum-insert-current-candidate)
  :commands completing-read)

We need prescient so we can have smarter sorting and filtering by default. Ontop of that, setting persistance in prescient makes it get better over time.

(use-package prescient
  :config
  (prescient-persist-mode +1)
  :after selectrum)
(use-package selectrum-prescient
  :init
  (selectrum-prescient-mode +1)
  :after selectrum)
;; enable company use of prescient
;; (company-prescient-mode +1)

;; enable magit to read with prescient
(setq magit-completing-read-function #'selectrum-completing-read)

Here we use posframe to make a prettier minibuffer. Posframe will work with EXWM with some tweaking, but I only stole some code for Ivy's posframe version, not selectrum's. So, I'll need to work on that.

(setq selectrum-display-action '(display-buffer-show-in-posframe))
(setq selectrum-display-action nil)

(defun display-buffer-show-in-posframe (buffer _alist)
  (frame-root-window
   (posframe-show buffer
                  :min-height 10
                  :min-width (/ (frame-width) 2)
                  :internal-border-width 1
                  :left-fringe 18
                  :right-fringe 18
                  :parent-frame nil
                  :z-group 'above
                  :poshandler 'posframe-poshandler-frame-center)))

(add-hook 'minibuffer-exit-hook 'posframe-delete-all)

This is similar but using mini-frame. Mini-frame works well, but not if using exwm. With exwm the X windows get displayed above the mini-frame, so the minibuffer isn't visible. Best to let Selectrum or Consult push the frame up and view the vertical completions below the frame.

(mini-frame-mode +1)
(mini-frame-mode -1)
(setq resize-mini-frames t)
(custom-set-variables
 '(mini-frame-show-parameters
   '((top . 400)
     (width . 0.7)
     (left . 0.5))))

;; workaround bug#44080, should be fixed in version 27.2 and above, see #169
(define-advice fit-frame-to-buffer (:around (f &rest args) dont-skip-ws-for-mini-frame)
  (cl-letf* ((orig (symbol-function #'window-text-pixel-size))
             ((symbol-function #'window-text-pixel-size)
              (lambda (win from to &rest args)
                (apply orig
                       (append (list win from
                                     (if (and (window-minibuffer-p win)
                                              (frame-root-window-p win)
                                              (eq t to))
                                         nil
                                       to))
                               args)))))
    (apply f args)))

VERTICO

Vertico is an alternative to Selectrum. Maybe using it will give me an even better experience over selectrum.

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

  ;; Different scroll margin
  ;; (setq vertico-scroll-margin 0)

  ;; Show more candidates
  (setq vertico-count 10)

  ;; Grow and shrink the Vertico minibuffer
  (setq vertico-resize t)

  ;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
  (setq vertico-cycle t)
  :general
  (general-def 'vertico-map
    "C-j" 'vertico-next
    "C-k" 'vertico-previous)
  )

;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
  :init
  (savehist-mode))

;; A few more useful configurations...
(use-package emacs
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; Alternatively try `consult-completing-read-multiple'.
  (defun crm-indicator (args)
    (cons (concat "[CRM] " (car args)) (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)

  ;; Emacs 28: Hide commands in M-x which do not work in the current mode.
  ;; Vertico commands are hidden in normal buffers.
  ;; (setq read-extended-command-predicate
  ;;       #'command-completion-default-include-p)

  ;; Enable recursive minibuffers
  (setq enable-recursive-minibuffers t))

CONSULT

Consult has a lot of nice functions like Ivy's Counsel functions (enhanced searching functions), lets set some of them in the keymap so they are easily used.

(use-package consult
  :after vertico
  :config
  (setq consult-narrow-key "'"
        consult-project-root-function 'projectile-project-root)

  (defun chris/consult-ripgrep-for-stepdata ()
    "Make ripgrep search hidden files for stepdata"
    (interactive)
    (let ((consult-ripgrep-args
           (concat "rg "
                   "--null "
                   "--line-buffered "
                   "--color=never "
                   "--max-columns=1000 "
                   "--path-separator / "
                   "--no-heading "
                   "--line-number "
                   ;; adding these to default
                   "--smart-case "
                   "--hidden "
                   ;; defaults
                   "."
                   )))
      (consult-ripgrep)))
  
  
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "si" 'consult-imenu
    "so" 'consult-org-heading
    "sm" 'bookmark-jump
    "sy" 'consult-yank-from-kill-ring))

MARGINALIA

Marginalia makes for some great decoration to our minibuffer completion items. Works great with Selectrum which does not have this out of the box.

(use-package marginalia
  :bind (:map minibuffer-local-map
              ("C-M-a" . marginalia-cycle)
              ;; :map embark-general-map
              ;;     ("A" . marginalia-cycle)
              )

  ;; The :init configuration is always executed (Not lazy!)
  :init

  ;; Must be in the :init section of use-package such that the mode gets
  ;; enabled right away. Note that this forces loading the package.
  (marginalia-mode)

  ;; When using Selectrum, ensure that Selectrum is refreshed when cycling annotations.
  ;; (advice-add #'marginalia-cycle :after
  ;;             (lambda () (when (bound-and-true-p selectrum-mode) (selectrum-exhibit))))

  ;; Prefer richer, more heavy, annotations over the lighter default variant.
  (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  :after vertico
  :config
  (setq marginalia--cache-size 60000))

Along with Marginalia, let's add in icons.

(use-package all-the-icons-completion
  :after vertico
  :config
  (all-the-icons-completion-mode))

Embark

Embark or do something.

(use-package embark
  :ensure t
  :general
  ('vertico-map
   "C-'" 'embark-act)
  ('normal 'org-mode-map
           "C-'" 'embark-act)

  :config
  (defun embark-which-key-indicator ()
    "An embark indicator that displays keymaps using which-key.
The which-key help message will show the type and value of the
current target followed by an ellipsis if there are further
targets."
    (lambda (&optional keymap targets prefix)
      (if (null keymap)
          (which-key--hide-popup-ignore-command)
        (which-key--show-keymap
         (if (eq (plist-get (car targets) :type) 'embark-become)
             "Become"
           (format "Act on %s '%s'%s"
                   (plist-get (car targets) :type)
                   (embark--truncate-target (plist-get (car targets) :target))
                   (if (cdr targets) "…" "")))
         (if prefix
             (pcase (lookup-key keymap prefix 'accept-default)
               ((and (pred keymapp) km) km)
               (_ (key-binding prefix 'accept-default)))
           keymap)
         nil nil t (lambda (binding)
                     (not (string-suffix-p "-argument" (cdr binding))))))))

  (setq embark-indicators
        '(embark-which-key-indicator
          embark-highlight-indicator
          embark-isearch-highlight-indicator))

  (defun embark-hide-which-key-indicator (fn &rest args)
    "Hide the which-key indicator immediately when using the completing-read prompter."
    (which-key--hide-popup-ignore-command)
    (let ((embark-indicators
           (remq #'embark-which-key-indicator embark-indicators)))
      (apply fn args)))

  (advice-add #'embark-completing-read-prompter
              :around #'embark-hide-which-key-indicator)
  )

(use-package embark-consult)

Company

(use-package company
  :config
  (global-company-mode +1)
  :custom
  (company-dabbrev-other-buffers t)
  (company-minimum-prefix-length 1)
  (company-idle-delay 0.1)
  :general
  (general-def '(normal insert) company-active-map
    "TAB" 'company-complete-selection
    "RET" 'company-complete-selection)
  (general-def '(normal insert) lsp-mode-map
    "TAB" 'company-indent-or-complete-common))

;; (use-package company-box
;;   :hook (company-mode . company-box-mode))
(use-package company-dict
  :after company)

Corfu

Trying out corfu instead of company

(use-package corfu
  :ensure t
  ;; Optional customizations
  :custom
  (corfu-cycle t)                ;; Enable cycling for `corfu-next/previous'
  (corfu-auto t)                 ;; Enable auto completion
  (corfu-separator ?\s)          ;; Orderless field separator
  (corfu-quit-no-match 'separator)      ;; Never quit, even if there is no match
  (corfu-preview-current 'insert)    ;; Enable current candidate preview
  (corfu-preselect-first nil)    ;; Enable candidate preselection
  (corfu-on-exact-match 'insert)     ;; Configure handling of exact matches
  (corfu-echo-documentation '(1.0 . 0.2)) ;; Disable documentation in the echo area
  (corfu-scroll-margin 5)        ;; Use scroll margin
  (corfu-count 15)
  (corfu-auto-prefix 2)

  ;; You may want to enable Corfu only for certain 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-/).
  :init
  (global-corfu-mode)
  :general
  ('corfu-map
   "C-j" 'corfu-next
   "C-k" 'corfu-previous
   "M-SPC" 'corfu-insert-separator))

;; Optionally use the `orderless' completion style. See `+orderless-dispatch'
;; in the Consult wiki for an advanced Orderless style dispatcher.
;; Enable `partial-completion' for files to allow path expansion.
;; You may prefer to use `initials' instead of `partial-completion'.
(use-package orderless
  :ensure t
  :init
  ;; Configure a custom style dispatcher (see the Consult wiki)
  ;; (setq orderless-style-dispatchers '(+orderless-dispatch)
  ;;       orderless-component-separator #'orderless-escapable-split-on-space)
  (setq completion-styles '(orderless)
        completion-category-defaults nil
        completion-category-overrides '((file (styles . (partial-completion)))))

  (defun flex-if-twiddle (pattern _index _total)
    (when (string-suffix-p "~" pattern)
      `(orderless-flex . ,(substring pattern 0 -1))))

  (defun first-initialism (pattern index _total)
    (if (= index 0) 'orderless-initialism))

  (defun without-if-bang (pattern _index _total)
    (cond
     ((equal "!" pattern)
      '(orderless-literal . ""))
     ((string-prefix-p "!" pattern)
      `(orderless-without-literal . ,(substring pattern 1)))))

  (setq orderless-matching-styles '(orderless-literal orderless-regexp)
        orderless-style-dispatchers '(flex-if-twiddle
                                      without-if-bang))
  )

;; Use dabbrev with Corfu!
(use-package dabbrev
  :after corfu
  ;; Swap M-/ and C-M-/
  :bind (("M-/" . dabbrev-completion)
         ("C-M-/" . dabbrev-expand)))

(use-package cape
  :ensure t
  ;; Bind dedicated completion commands
  :bind (("C-c p p" . completion-at-point) ;; capf
         ("C-c p t" . complete-tag)        ;; etags
         ("C-c p d" . cape-dabbrev)        ;; or dabbrev-completion
         ("C-c p f" . cape-file)
         ("C-c p k" . cape-keyword)
         ("C-c p s" . cape-symbol)
         ("C-c p a" . cape-abbrev)
         ("C-c p i" . cape-ispell)
         ("C-c p l" . cape-line)
         ("C-c p w" . cape-dict)
         ("C-c p \\" . cape-tex)
         ("C-c p _" . cape-tex)
         ("C-c p ^" . cape-tex)
         ("C-c p &" . cape-sgml)
         ("C-c p r" . cape-rfc1345))
  :init
  ;; Add `completion-at-point-functions', used by `completion-at-point'.
  (add-to-list 'completion-at-point-functions #'cape-file)
  ;; (add-to-list 'completion-at-point-functions #'cape-tex)
  (add-to-list 'completion-at-point-functions #'cape-keyword)
  (add-to-list 'completion-at-point-functions #'cape-dabbrev)
  ;;(add-to-list 'completion-at-point-functions #'cape-sgml)
  ;;(add-to-list 'completion-at-point-functions #'cape-rfc1345)
  ;;(add-to-list 'completion-at-point-functions #'cape-abbrev)
  (add-to-list 'completion-at-point-functions #'cape-ispell)
  ;;(add-to-list 'completion-at-point-functions #'cape-dict)
  ;; (add-to-list 'completion-at-point-functions #'cape-symbol)
  ;;(add-to-list 'completion-at-point-functions #'cape-line)

  :config
  (setq cape-dabbrev-min-length 2)  
  )

Devdocs

Devdocs.io is a pretty great place to see documentation on nearly any developer technology. Devdocs.el will bring all that documentation right inside Emacs.

(use-package devdocs
  :general
  (chris/leader-keys 'normal
    "hd" 'devdocs-lookup))

YASnippet

YASnippet is a templating system. It's powerful.

(use-package yasnippet
  :config
  (setq yas-snippet-dirs (list (expand-file-name "yasnippets/" user-emacs-directory)))
  (yas-global-mode 1))

Tempel

Tempel is another templating system. Also perhaps even more powerful with it's elisp way of creating snippets, but elisp is tougher to work around. But, I'll give it a try.

(use-package tempel
  :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand
         ("M-'" . tempel-insert)
         ("C-M-<return>" . tempel-done))

  :init

  ;; Setup completion at point
  (defun tempel-setup-capf ()
    ;; Add the Tempel Capf to `completion-at-point-functions'. `tempel-expand'
    ;; only triggers on exact matches. Alternatively use `tempel-complete' if
    ;; you want to see all matches, but then Tempel will probably trigger too
    ;; often when you don't expect it.
    ;; NOTE: We add `tempel-expand' *before* the main programming mode Capf,
    ;; such that it will be tried first.
    (setq-local completion-at-point-functions
                (cons #'tempel-complete
                      completion-at-point-functions)))

  (add-hook 'prog-mode-hook 'tempel-setup-capf)
  (add-hook 'text-mode-hook 'tempel-setup-capf)

  ;; Optionally make the Tempel templates available to Abbrev,
  ;; either locally or globally. `expand-abbrev' is bound to C-x '.
  ;; (add-hook 'prog-mode-hook #'tempel-abbrev-mode)
  ;; (tempel-global-abbrev-mode)
  :general
  (chris/leader-keys
    "it" 'tempel-insert)
)

Projectile

I'm going to use projectile to keep my projects inline.

(use-package projectile
  :ensure t
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "op" 'projectile-switch-open-project
    "gc" 'projectile-compile-project
    "gr" 'projectile-run-project
    "fp" 'project-find-file
    "fP" 'project-switch-project))

HTTPD

In order to view created websites, I'll use simple-httpd to get a web server running in emacs for preview.

(use-package simple-httpd
  :ensure t)

Navigation

Avy

Avy provides a lot of functions to search through the current buffer. Most of the time I use evil or consult functions to find what I'm looking for, but avy provides a lot of small movements that are more useful for visible movements.

(use-package avy
  :after evil)

These are some evil bindings to avy.

(use-package evil-avy
  :after avy
  :general
  (general-define-key
   :states 'normal
   :keymaps '(override magit-mode-map)
   "F" 'magit-pull)
  (general-def 'normal
    "gl" 'avy-goto-line))

Ace-Link

Ace link provides an avy like search for links. Upon using the keybindings presented it opens the url.

(use-package ace-link
  :after avy
  :general
  (general-def 'normal
    "gL" 'ace-link))

Window Management

(setq display-buffer-alist
      '(("\\*e?shell\\*"
         (display-buffer-in-side-window)
         (side . bottom)
         (window-height . 0.25))
        ("*helpful*"
         (display-buffer-in-side-window)
         (side . right)
         (window-width . 0.4))
        ("*compilation*"
         (display-buffer-in-side-window)
         (side . right)
         (window-width . 0.4))
        ("*org-roam*"
         (display-buffer-in-side-window)
         (side . right)
         (window-width . 0.4))
        ("\\*elfeed-entry\\*"
         (display-buffer-in-side-window)
         (side . bottom)
         (window-height . 0.60))
        ("*Agenda Commands*"
         (display-buffer-in-side-window)
         (side . right)
         (window-width . 0.30))
        ("\\*Bongo-Elfeed Queue\\*"
         (display-buffer-in-side-window)
         (side . bottom)
         (window-height . 0.25))))

Since I like to make my window manager handle a lot of the window management, I will create a helper function for closing windows.

(defun chris/kill-buffer-frame ()
  "Kills the active buffer and frame"
  (interactive)
  (kill-this-buffer)
  (delete-frame))

Ace Window

(use-package ace-window
  :config (ace-window-display-mode)
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "ww" '(ace-window :which-key "select window")))

Help

(use-package helpful
  :ensure nil
  :commands (helpful-callable helpful-variable helpful-command helpful-key)
  :general
  (general-def 'normal 'helpful-mode-map
    "q" 'helpful-kill-buffers)
  :config
  (defun helpful--autoloaded-p (sym buf)
    "Return non-nil if function SYM is autoloaded."
    (-when-let (file-name (buffer-file-name buf))
      (setq file-name (s-chop-suffix ".gz" file-name))
      (help-fns--autoloaded-p sym file-name)))

  (defun helpful--skip-advice (docstring)
    "Remove mentions of advice from DOCSTRING."
    (let* ((lines (s-lines docstring))
           (relevant-lines
            (--take-while
             (not (or (s-starts-with-p ":around advice:" it)
                      (s-starts-with-p "This function has :around advice:" it)))
             lines)))
      (s-trim (s-join "\n" relevant-lines)))))

Format

(use-package format-all
  :config
  (format-all-mode +1)
  (setq format-all-formatters '("Emacs Lisp" emacs-lisp))
  :defer 1)

Languages

Before getting into languages, often times compilation of things use ansi-colors, so in order to make sure those characters get rendered properly, here we make sure that those colors work ok.

(defvar read-symbol-positions-list nil)
(add-hook 'comint-mode-hook 'ansi-color-for-comint-mode-on)

Let's also set hl-line-mode to be on for comint and prog modes

(add-hook 'comint-mode-hook 'hl-line-mode)
(add-hook 'prog-mode-hook 'hl-line-mode)
(add-hook 'prog-mode-hook 'hs-minor-mode)

Tree Sitter

I'm gonna try this to speed up emacs and make it nicer

(use-package tree-sitter)
(use-package tree-sitter-langs)

C++

In c++ I just want to make sure lsp is called when enter c++-mode

(add-hook 'c++-mode-hook 'lsp)

Lua

Since I use the Awesome WM I thought it'd be good to have lua around. It's also in a lot of things.

(use-package lua-mode
  :mode ("\\.lua\\'" . lua-mode))

Nix

I've been transitioning more of my OS to NixOS. Let's get nix-mode working.

(use-package nix-mode
  :mode "\\.nix\\'")

LSP

LSP is useful…

(use-package lsp-mode
  :commands (lsp lsp-deferred)
  :custom
  (lsp-completion-provider :none)
  :hook
  (c++-mode . lsp-deferred)
  (lsp-completion-mode . my/lsp-mode-setup-completion)
  :init
  (setq lsp-keymap-prefix "C-c l")
  (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
          '(flex)))
  :config
  (setq lsp-lens-enable t
        lsp-signature-auto-activate nil
        read-process-output-max (* 1024 1024))
  (lsp-enable-which-key-integration t)
  (add-to-list 'lsp-language-id-configuration '(qml-mode . "qml"))
  (lsp-register-client
   (make-lsp-client :new-connection (lsp-stdio-connection "qml-lsp")
                    :activation-fn (lsp-activate-on "qml")
                    :server-id 'qml))
  :general
  (general-def 'normal c++-mode-map
    "gf" 'lsp-clangd-find-other-file))

(use-package lsp-ui
  :hook (lsp-mode . lsp-ui-mode)
  :custom
  (lsp-ui-doc-position 'at-point))

(use-package lsp-treemacs
  :after lsp)

Fennel

I use fennel to build my awesomewm config. So, we'll need that downloaded.

(use-package fennel-mode
  :mode ("\\.fnl\\'" . fennel-mode))

Friar

Friar is a fennel repl in the awesome repl. It allows you to interact with AwesomeWM from inside emacs.

(use-package friar 
  :straight (:host github :repo "warreq/friar" :branch "master"
             :files (:defaults "*.lua" "*.fnl"))
  :after fennel-mode)

Yaml

I do a lot of docker management so having yaml is necessary

(use-package yaml-mode
  :mode ("\\.yml\\'" . yaml-mode))

Docker itself

(use-package docker
  :defer t
  :config
  (setq docker-run-as-root t))

Let's make sure docker is capable of using tramp.

(use-package docker-tramp
  :after docker)

Still need dockerfile-mode in order to be able to edit dockerfiles.

Fish

Fish is my preferred shell and made some scripts in it so I keep this to be able to edit those scripts.

(use-package fish-mode
  :mode ("\\.fish\\'" . fish-mode))

Markdown

It's probably smart to have markdown.

(use-package markdown-mode
  :mode ("\\.md\\'" . markdown-mode)
  :config
  (setq markdown-fontify-code-blocks-natively t))

QML

I make some apps in Qt 5 so QML is a needed language although the support in Emacs is lacking.

(use-package qml-mode
  :mode ("\\.qml\\'" . qml-mode))

;; (use-package company-qml
;;   :after qml-mode
;;   :config
;;   ;; (add-to-list 'company-backends 'company-qml)
;;   )

;; (setq company-backends
;;       '(company-bbdb company-semantic company-cmake company-capf company-clang company-files
;;               (company-dabbrev-code company-gtags company-etags company-keywords)
;;               company-oddmuse company-dabbrev))

;; (use-package qt-pro-mode
;;   :after qml-mode)

CSV

Sometimes I need to edit CSV files quickly

(use-package csv-mode
  :mode ("\\.csv\\'" . csv-mode))

Restclient

Sometimes dealing with REST APIs it's easiest to do this incrementally with restclient.el

(use-package restclient
  :commands (restclient-mode))

Let's also add org-babel support for this to create documentation easier.

(use-package ob-restclient
  :after org)

Dart/Flutter

I may get into flutter development over using felgo….. but i'm not happy about it….

(use-package dart-mode
  :mode ("\\.dart\\'" . dart-mode)
  :hook (dart-mode . lsp-deferred)
  :general
  (general-def 'normal dart-mode-map
    "gr" 'flutter-run-or-hot-reload
    "gR" 'lsp-dart-dap-flutter-hot-restart))

(use-package lsp-dart)
(use-package flutter
  :after dart
  :general
  (chris/leader-keys dart-mode-map
    "rf" 'flutter-run-or-hot-reload))
(use-package hover
  :after dart)

Let's also add the android-sdk tools to emacs' path.

(add-to-list 'exec-path "/opt/android-sdk/cmdline-tools/latest/bin")

File Management

Dired

I'm making a small function in here to open files in the appropriate program using XDG defaults. This is like opening odt files in Libreoffice or mp4 files in MPV.

(use-package dired
  :ensure nil
  :straight nil
  :config
  (defun chris/dired-open-xdg ()
    "Open the file-at-point in the appropriate program"
    (interactive)
    (let ((file (file-truename (ignore-errors (dired-get-file-for-visit)))))
      (message file)
      (call-process "xdg-open" nil 0 nil file)))

  (setq dired-dwim-target t
        delete-by-moving-to-trash t)

  (setq dired-listing-switches "-aoh --group-directories-first")
  (setq dired-hide-details-hide-symlink-targets nil
        dired-kill-when-opening-new-dired-buffer t)
  (add-hook 'dired-mode-hook #'dired-hide-details-mode)

  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "od" '(dired-jump :which-key "open dired here")
    "oD" '(dired :which-key "open dired select"))
  ('normal dired-mode-map
    "q" 'kill-this-buffer
    "C-<return>" 'chris/dired-open-xdg
    "C-'" 'embark-act))

We need a function to copy the full filename to kill-ring

(defun chris/dired-yank-filename ()
  "Get the full filename from file at point and put into kill-ring"
  (interactive)
  (let* ((file (dired-get-filename)))
    (clipboard-kill-ring-save nil nil file)))
(use-package all-the-icons-dired
  :hook (dired-mode . all-the-icons-dired-mode))
(use-package dired-single
  :after dired
  :general
  (general-def 'normal dired-mode-map
    "h" 'dired-single-up-directory
    "l" 'dired-single-buffer))
(use-package dired-rainbow
  :after dired
  :config
  (defconst chris/dired-media-files-extensions
    '("mp3" "mp4" "MP3" "MP4" "avi" "mpg" "flv" "ogg" "opus")
    "Media files.")

  (defconst chris/dired-image-files-extensions
    '("png" "jpg" "PNG" "JPG" "jpeg" "JPEG" "gif" "GIF")
    "image files")

  (dired-rainbow-define html "#4e9a06" ("htm" "html" "xhtml"))
  (dired-rainbow-define media "#f3f99d" chris/dired-media-files-extensions)
  (dired-rainbow-define image "#5af78e" chris/dired-image-files-extensions)

  (dired-rainbow-define log (:inherit default :italic t) ".*\\.log"))
(use-package diredfl
  :after dired
  :config (diredfl-global-mode +1))
(use-package dired-rsync
  :general
  (general-def 'normal dired-mode-map
    "C" 'dired-rsync))
Dirvish

Let's try using dirvish as a kind of ranger

(use-package dirvish
  :after dired
  :custom
  (dirvish-bookmarks-alist
   '(("h" "~/"                          "Home")
     ("d" "~/Downloads/"                "Downloads")
     ("p" "~/Pictures/"                 "Pictures")
     ("D" "/dev/"                       "dev")
     ("t" "~/.local/share/Trash/files/" "TrashCan")))
  :config
  (dirvish-override-dired-mode)
  ;; (dirvish-peek-mode)
  (setq dirvish-attributes '(all-the-icons file-size))

  (dirvish-define-preview exa (file)
    "Uses `exa' to generate directory preview."
    (when (file-directory-p file) ; we only interest in directories here
      `(shell . ("exa" "--color=always" "-al" ,file))))
  (add-to-list 'dirvish-preview-dispatchers 'exa)

  (set-face-attribute 'ansi-color-blue nil :foreground "#FFFFFF")

  (defun chris/dirvish-quit ()
    "quit a fullscreen dirvish if it's open, else do a normal kill buffer"
    (interactive)
    (if (dirvish-dired-p)
        (dirvish-quit-h)
      (with-current-buffer
          (current-buffer)
        (dirvish-toggle-fullscreen)
        (dirvish-quit-h))))
  :general
  (chris/leader-keys 'normal 'override
    "od" 'dirvish-dired :which-key "open dirvish here")
  ('normal 'dirvish-mode-map
           "gf" 'dirvish-toggle-fullscreen
           "RET" 'dirvish-find-file
           "h" 'dirvish-up-directory
           "l" 'dirvish-find-file
           "b" 'dirvish-goto-bookmark
           "?" 'dirvish-top-level-menu
           "a" 'dirvish-file-info-menu
           "A" 'dirvish-mark-actions-menu
           "M" 'dirvish-marking-menu
           "q" 'chris/dirvish-quit))

Tramp

(require 'tramp)
(add-to-list 'tramp-default-proxies-alist
             '(nil "\\`root\\'" "/ssh:%h:"))
(add-to-list 'tramp-default-proxies-alist
             '((regexp-quote (system-name)) nil nil))

Ledger

Ledger mode

(use-package ledger-mode)

MU4E

(use-package mu4e
  :ensure nil
  :config
  (setq mail-user-agent 'mu4e-user-agent)
  (setq mu4e-maildir "~/Maildir"
        user-full-name "Chris Cochrun"
        mu4e-change-filenames-when-moving t
        mu4e-get-mail-command "mbsync -a"
        mu4e-update-interval (* 15 60)
        mu4e-attachment-dir "/home/chris/nextcloud/home/Documents/attachments"
        mu4e-completing-read-function #'completing-read)

  (setq mu4e-contexts
        (list
         (make-mu4e-context
          :name "work"
          :match-func
          (lambda (msg)
            (when msg
              (string-prefix-p "/office" (mu4e-message-field msg :maildir))))
          :vars '((user-mail-address      . "chris@tfcconnection.org")
                  (mu4e-sent-folder       . "/office/Sent Items/")
                  (mu4e-drafts-folder     . "/office/Drafts")
                  (mu4e-trash-folder      . "/office/Deleted Items")
                  (mu4e-refile-folder     . "/office/Archive")
                  (smtpmail-smtp-user     . "chris@tfcconnection.org")
                  (smtpmail-starttls-credentials . '(("smtp.office365.com" 587 nil nil)))
                  (smtpmail-auth-credentials . '(("smtp.office365.com" 587 "chris@tfcconnection.org" nil)))
                  (smtpmail-smtp-server . "smtp.office365.com")
                  (mu4e-compose-signature . "Praising God in all things,\nChris Cochrun")))
         (make-mu4e-context
          :name "outlook"
          :match-func
          (lambda (msg)
            (when msg
              (string-prefix-p "/outlook" (mu4e-message-field msg :maildir))))
          :vars '((user-mail-address      . "chris.cochrun@outlook.com")
                  (mu4e-sent-folder       . "/outlook/Sent/")
                  (mu4e-drafts-folder     . "/outlook/Drafts")
                  (mu4e-trash-folder      . "/outlook/Deleted")
                  (mu4e-refile-folder     . "/outlook/Archive")
                  (smtpmail-smtp-user     . "chris.cochrun@outlook.com")
                  (mu4e-compose-signature . "Praising God in all things,\nChris Cochrun")))
         (make-mu4e-context
          :name "personal"
          :match-func
          (lambda (msg)
            (when msg
              (string-prefix-p "/cochrun" (mu4e-message-field msg :maildir))))
          :vars '((user-mail-address      . "chris@cochrun.xyz")
                  (mu4e-sent-folder       . "/cochrun/Sent/")
                  (mu4e-drafts-folder     . "/cochrun/Drafts")
                  (mu4e-trash-folder      . "/cochrun/Trash")
                  (mu4e-refile-folder     . "/cochrun/Archive")
                  (smtpmail-smtp-user     . "chris@cochrun.xyz")
                  (smtpmail-starttls-credentials . '(("mail.cochrun.xyz" 587 nil nil)))
                  (smtpmail-auth-credentials . '(("mail.cochrun.xyz" 587 "chris@cochrun.xyz" nil)))
                  (smtpmail-smtp-server . "mail.cochrun.xyz")
                  (mu4e-compose-signature . "Praising God in all things,\nChris Cochrun")))
         (make-mu4e-context
          :name "gmail"
          :match-func
          (lambda (msg)
            (when msg
              (string-prefix-p "/gmail" (mu4e-message-field msg :maildir))))
          :vars '((user-mail-address      . "ccochrun21@gmail.com")
                  (mu4e-sent-folder       . "/gmail/[Gmail].Sent Mail/")
                  (smtpmail-smtp-user     . "ccochrun21@gmail.com")
                  (mu4e-compose-signature . "Praising God in all things,\nChris Cochrun")))))

  ;; Add the ability to send email
  (setq message-send-mail-function 'smtpmail-send-it
        starttls-use-gnutls t
        smtpmail-default-smtp-server "mail.cochrun.xyz"
        smtpmail-smtp-service 587)

  ;; shortcuts in the jumplist by pressing "J" in the mu4e buffer
  (setq mu4e-maildir-shortcuts
        '((:maildir "/office/Archive"                :key ?a)
          (:maildir "/office/INBOX"                  :key ?i)
          (:maildir "/cochrun/INBOX"                  :key ?p)
          (:maildir "/outlook/INBOX"                 :key ?l)
          (:maildir "/office/Junk Email"             :key ?j)
          (:maildir "/office/INBOX/Website Forms"    :key ?f)
          (:maildir "/gmail/INBOX"                   :key ?g)
          (:maildir "/office/Sent Items"             :key ?s)))

  ;; (add-to-list mu4e-headers-actions ("org capture message" . mu4e-org-store-and-capture))

  (setq mu4e-bookmarks
        '((:name "Unread messages"
                 :query "flag:unread AND NOT flag:trashed AND NOT maildir:\"/outlook/Junk\" AND NOT maildir:\"/office/Junk Email\" AND NOT maildir:\"/outlook/Deleted\" AND NOT maildir:\"/office/Deleted Items\""
                 :key 117)
          (:name "Today's messages"
                 :query "date:today..now"
                 :key 116)
          (:name "Last 7 days"
                 :query "date:7d..now"
                 :hide-unread t
                 :key 119)
          (:name "Messages with images"
                 :query "mime:image/*"
                 :key 112)))

  (setq mu4e-mu-binary "/usr/bin/mu"
        mu4e-view-prefer-html nil
        shr-color-visible-luminance-min 80)

  (setq mu4e-use-fancy-chars t
        mu4e-headers-draft-mark '("D" . "")
        mu4e-headers-flagged-mark '("F" . "")
        mu4e-headers-new-mark '("N" . " ")
        mu4e-headers-passed-mark '("P" . "")
        mu4e-headers-replied-mark '("R" . "")
        mu4e-headers-seen-mark '("S" . " ")
        mu4e-headers-trashed-mark '("T" . " ")
        mu4e-headers-attach-mark '("a" . " ")
        mu4e-headers-encrypted-mark '("x" . "")
        mu4e-headers-signed-mark '("s" . " ")
        mu4e-headers-unread-mark '("u" . " "))

  (setq mu4e-headers-fields
        '((:human-date . 12)
          (:flags . 6)
          (:from . 22)
          (:subject)))

  (setq mu4e-view-actions
        '(("capture message" . mu4e-action-capture-message)
          ("view in browser" . mu4e-action-view-in-browser)
          ("show this thread" . mu4e-action-show-thread)))

  (defun chris/setup-mu4e-headers ()
    (toggle-truncate-lines +1)
    (display-line-numbers-mode -1))


  (defun chris/setup-mu4e-view ()
    (display-line-numbers-mode -1)
    (toggle-truncate-lines +1)
    (setq visual-fill-column-center-text t)
    (setq visual-fill-column-width 100)
    (visual-fill-column-mode +1))

  (remove-hook 'mu4e-main-mode-hook '(display-line-numbers-mode -1))
  (add-hook 'mu4e-headers-mode-hook #'chris/setup-mu4e-headers)
  (add-hook 'mu4e-view-mode-hook #'chris/setup-mu4e-view)

  
  (mu4e t)
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "om" 'mu4e)
  (general-def 'normal mu4e-view-mode-map
    "ga" 'mu4e-view-save-attachments))

Let's add org-msg to write emails in org-mode

(use-package org-msg
  :hook (mu4e-compose-mode . org-msg-edit-mode)
  :config
  (org-msg-mode)
  (setq org-msg-startup "inlineimages"
        org-msg-greeting-name-limit 3
        org-msg-default-alternatives '(html text)))

Calendar

(use-package calfw
  :commands chris/calfw-calendar-open
  :config
  (defun chris/calfw-calendar-open ()
    (interactive)
    (cfw:open-calendar-buffer
     :contents-sources
     (list
      (cfw:org-create-source
       "Cyan")  ; org-agenda source
      (cfw:ical-create-source
       "NV" "https://www.nvhuskies.org/vnews/display.v?ical" "Green")  ; School Calendar
      (cfw:ical-create-source
       "Outlook" "https://outlook.office365.com/owa/calendar/62a0d491bec4430e825822afd2fd1c01@tfcconnection.org/9acc5bc27ca24ce7a900c57284959f9d8242340735661296952/S-1-8-2197686000-2519837503-3687200543-3873966527/reachcalendar.ics" "Yellow")  ; Outlook Calendar
      )))
  (custom-set-faces
   '(cfw:face-title ((t (:weight bold :height 2.0 :inherit fixed-pitch))))
   '(cfw:face-header ((t (:slant italic :weight bold))))
   '(cfw:face-sunday ((t :weight bold)))
   '(cfw:face-saturday ((t :weight bold)))
   '(cfw:face-holiday ((t :weight bold)))
   ;; '(cfw:face-grid ((t :foreground "DarkGrey")))
   ;; '(cfw:face-default-content ((t :foreground "#bfebbf")))
   ;; '(cfw:face-periods ((t :foreground "cyan")))
   '(cfw:face-day-title ((t :background nil)))
   '(cfw:face-default-day ((t :weight bold :inherit cfw:face-day-title)))
   '(cfw:face-annotation ((t :inherit cfw:face-day-title)))
   '(cfw:face-disable ((t :inherit cfw:face-day-title)))
   '(cfw:face-today-title ((t :weight bold)))
   '(cfw:face-today ((t :weight bold)))
   ;; '(cfw:face-select ((t :background "#2f2f2f")))
   '(cfw:face-toolbar ((t :foreground "Steelblue4" :background "Steelblue4")))
   '(cfw:face-toolbar-button-off ((t :weight bold)))
   '(cfw:face-toolbar-button-on ((t :weight bold))))
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "oc" 'chris/calfw-calendar-open)
  (general-def cfw:calendar-mode-map
    "q" 'kill-this-buffer
    "RET" 'cfw:show-details-command)
  (general-def 'normal cfw:details-mode-map
    "q" 'cfw:details-kill-buffer-command))

Calfw-Org

Here we can use org as a way to create calfw sources in the calendar view.

(use-package calfw-org
  :after calfw)

Calfw-ical

Here we setup an easy way to use ical as a source for calendar views.

(use-package calfw-ical
  :after calfw)

Org-Caldav-sync

I'd like to sync my org-files to caldav and hopefully use more native android apps to manage tasks and reminders.

(use-package org-caldav
  :after org
  :config
  (setq org-caldav-url "https://staff.tfcconnection.org/remote.php/dav/calendars/chris"
        org-caldav-calendar-id "org"
        org-caldav-inbox "/home/chris/org/todo/inbox.org"
        org-caldav-files '("/home/chris/org/todo/todo.org"
                          "/home/chris/org/todo/notes.org"
                          "/home/chris/org/todo/prayer.org")
        org-icalendar-alarm-time 15
        org-icalendar-use-scheduled '(todo-start event-if-todo)))

Org Notifications

I'd really like to have notifications for when things are scheduled so that I get my butt to working.

(use-package org-notifications
  :after org
  :config
  (setq org-notifications-notify-before-time 600)
  (org-notifications-start))
(use-package org-wild-notifier
  :after org
  :config
  (setq alert-default-style 'libnotify)
  (org-wild-notifier-mode +1))

Magit

Use magit, because why wouldn't you? duh!

(use-package magit
  :ensure nil
  :commands (magit-status magit-get-current-branch)
  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "g g" 'magit-status)
  :custom
  (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1))

Eshell

Let's add our own eshell prompt. and set the password cache to a significantly higher time in order to not need to constantly reuse my password.

(use-package eshell
  :ensure nil
  :straight nil
  :config
  (require 'em-tramp)

  (with-eval-after-load 'esh-module   ;; REVIEW: It used to work, but now the early `provide' seems to backfire.
    (unless (boundp 'eshell-modules-list)
      (load "esh-module"))   ;; Don't print the banner.
    (push 'eshell-tramp eshell-modules-list))

  (setq password-cache t
        password-cache-expiry 3600)

  (setq eshell-history-size 1024)


  ;;; Extra execution information
  (defvar chris/eshell-status-p t
    "If non-nil, display status before prompt.")
  (defvar chris/eshell-status--last-command-time nil)
  (make-variable-buffer-local 'chris/eshell-status--last-command-time)
  (defvar chris/eshell-status-min-duration-before-display 0
    "If a command takes more time than this, display its duration.")

  (defun chris/eshell-status-display ()
    (if chris/eshell-status--last-command-time
        (let ((duration (time-subtract (current-time) chris/eshell-status--last-command-time)))
          (setq chris/eshell-status--last-command-time nil)
          (when (> (time-to-seconds duration) chris/eshell-status-min-duration-before-display)
            (format "  %.3fs %s"
                    (time-to-seconds duration)
                    (format-time-string "| %F %T" (current-time)))))
      (format "  0.000s")))

  (defun chris/eshell-status-record ()
    (setq chris/eshell-status--last-command-time (current-time)))

  (add-hook 'eshell-pre-command-hook 'chris/eshell-status-record)

  (setq eshell-prompt-function
        (lambda nil
          (let ((path (abbreviate-file-name (eshell/pwd))))
            (concat
             (if (or (string= system-name "archdesktop") (string= system-name "syl"))
                 nil
               (format
                (propertize "\n(%s@%s)" 'face '(:foreground "#606580"))
                (propertize (user-login-name) 'face '(:inherit compilation-warning))
                (propertize (system-name) 'face '(:inherit compilation-warning))))
             (if (and (require 'magit nil t) (or (magit-get-current-branch) (magit-get-current-tag)))
                 (let* ((root (abbreviate-file-name (magit-rev-parse "--show-toplevel")))
                        (after-root (substring-no-properties path (min (length path) (1+ (length root))))))
                   (format
                    (propertize "\n[ %s | %s@%s ]" 'face font-lock-comment-face)
                    (propertize root 'face `(:inherit org-warning))
                    (propertize after-root 'face `(:inherit org-level-1))
                    (propertize (or (magit-get-current-branch) (magit-get-current-tag)) 'face `(:inherit org-macro))))
               (format
                (propertize "\n[%s]" 'face font-lock-comment-face)
                (propertize path 'face `(:inherit org-level-1))))
             (when chris/eshell-status-p
               (propertize (or (chris/eshell-status-display) "") 'face font-lock-comment-face))
             (propertize "\n" 'face '(:inherit org-todo :weight ultra-bold))
             " "))))

  ;;; If the prompt spans over multiple lines, the regexp should match
  ;;; last line only.
  (setq-default eshell-prompt-regexp "^ ")
  (setq eshell-destroy-buffer-when-process-dies t)

  (defun chris/pop-eshell ()
    "Make an eshell frame on the bottom"
    (interactive)
    (unless pop-eshell
      (setq pop-eshell (eshell 100))
      (with-current-buffer pop-eshell
        (eshell/clear-scrollback)
        (rename-buffer "*eshell-pop*")
        (display-buffer-in-side-window pop-eshell '((side . bottom))))))

  (setq eshell-banner-message "")

  (setq eshell-path-env "/usr/local/bin:/usr/bin:/opt/android-sdk/cmdline-tools/latest/bin")

  ;; this makes it so flutter works properly
  (setenv "ANDROID_SDK_ROOT" "/opt/android-sdk")
  (setenv "CHROME_EXECUTABLE" "/usr/bin/qutebrowser")
  (setenv "JAVA_HOME" "/usr/lib/jvm/default")

  (add-hook 'eshell-mode-hook '(display-line-numbers-mode -1))

  (setq eshell-command-aliases-list
      '(("q" "exit")
        ("f" "find-file $1")
        ("ff" "find-file $1")
        ("d" "dired $1")
        ("bd" "eshell-up $1")
        ("rg" "rg --color=always $*")
        ("ll" "ls -lah $*")
        ("gg" "magit-status")
        ("clear" "clear-scrollback")
        ("!c" "eshell-previous-input 2")
        ("yay" "paru $1")
        ("yeet" "paru -Rns $1")))

  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "oe" 'eshell)
  (general-def '(normal insert) eshell-mode-map
    "C-d" 'kill-buffer-and-window))

Sly

Using sly makes a lot better common-lisp interaction within emacs.

(use-package sly
  :mode
  ("\\.lisp\\'" . sly-mode)
  ("\\.lisp\\'" . lisp-mode)
  :config
  (defun chris/start-nyxt-repl ()
      "Start the repl and sly connection for nyxt"
    (interactive)
    (sly-connect "localhost" 4006)))

PDF-Tools

Let's use pdf-tools for a lot better interaction with pdfs.

(use-package pdf-tools
  :straight (:host github
                   :repo "flatwhatson/pdf-tools"
                   :branch "fix-macros")
  :defer 1
  :config
  (pdf-tools-install)
  (custom-set-variables '(pdf-misc-print-program "/usr/bin/lpr")
                        '(pdf-misc-print-program-args (quote ("-o media=Letter" "-o fitplot"))))
  (add-hook 'pdf-view-mode 'pdf-view-fit-page-to-window))

EPUB

(use-package nov
  :mode ("\\.epub\\'" . nov-mode)
  :config

  (defun chris/setup-nov-mode 
    (interactive)
    (visual-fill-column-mode)
    (display-line-numbers-mode -1)
    (variable-pitch-mode +1)
    (setq visual-fill-column-width 100
          visual-fill-column-center-text t))

  (add-hook 'nov-mode-hook 'chris/setup-nov-mode))

EAF (Emacs Application Framework)

(use-package eaf
  :straight (:host github :repo "emacs-eaf/emacs-application-framework"
                   :files ("*.el" "*.py" "core" "*.json" "app" "*"))
  :defer 1
  :custom
  (eaf-browser-dark-mode t)
  (eaf-browser-continue-where-left-off t)
  (eaf-browser-enable-adblocker t))

Elfeed

(use-package elfeed
  :commands (elfeed)
  :config
  (defvar chris/elfeed-bongo-playlist "*Bongo-Elfeed Queue*"
    "Name of the Elfeed+Bongo multimedia playlist.")

  (defun chris/elfeed-bongo-insert-item ()
    "Insert `elfeed' multimedia links in `bongo' playlist buffer.

The playlist buffer has a unique name so that it will never
interfere with the default `bongo-playlist-buffer'."
    (interactive)
    (let* ((entry (elfeed-search-selected :ignore-region))
           (link (elfeed-entry-link entry))
           (enclosure (elt (car (elfeed-entry-enclosures entry)) 0))
           (url (if (string-prefix-p "https://thumbnails" enclosure)
                    link
                  (if (string= enclosure nil)
                      link
                    enclosure)))
           (title (elfeed-entry-title entry))
           (bongo-pl chris/elfeed-bongo-playlist)
           (buffer (get-buffer-create bongo-pl)))
      (message "link is %s" link)
      (message "enclosure is %s" enclosure)
      (message "url is %s" url)
      (message "title is %s" title)
      (elfeed-search-untag-all-unread)
      (unless (bongo-playlist-buffer)
        (bongo-playlist-buffer))
      (display-buffer buffer)
      (with-current-buffer buffer
        (when (not (bongo-playlist-buffer-p))
          (bongo-playlist-mode)
          (setq-local bongo-library-buffer (get-buffer "*elfeed-search*"))
          (setq-local bongo-enabled-backends '(mpv))
          (bongo-progressive-playback-mode))
        (goto-char (point-max))
        (bongo-insert-uri url (format "%s ==> %s" title url))
        (let ((inhibit-read-only t))
          (delete-duplicate-lines (point-min) (point-max)))
        (bongo-recenter))
      (message "Enqueued %s “%s” in %s"
               (if enclosure "podcast" "video")
               (propertize title 'face 'italic)
               (propertize bongo-pl 'face 'bold))))

  (defun chris/elfeed-bongo-insert-link ()
    "Inserts the hovered link into bongo for playback in mpv"
    (interactive)
    (let* ((link (shr--current-link-region))
           (entry elfeed-show-entry)
           (title (elfeed-entry-title entry))
           (url (get-text-property (point) 'shr-url))
           (bongo-pl chris/elfeed-bongo-playlist)
           (buffer (get-buffer-create bongo-pl)))
      (message "url is %s" url)
      (message "title is %s" title)
      (unless (bongo-playlist-buffer)
        (bongo-playlist-buffer))
      (display-buffer buffer)
      (with-current-buffer buffer
        (when (not (bongo-playlist-buffer-p))
          (bongo-playlist-mode)
          (setq-local bongo-library-buffer (get-buffer "*elfeed-search*"))
          (setq-local bongo-enabled-backends '(mpv))
          (bongo-progressive-playback-mode))
        (goto-char (point-max))
        (bongo-insert-uri url (format "%s ==> %s" title url))
        (let ((inhibit-read-only t))
          (delete-duplicate-lines (point-min) (point-max)))
        (bongo-recenter))
      (message "Enqueued item “%s” in %s"
               (propertize title 'face 'italic)
               (propertize bongo-pl 'face 'bold))))
  
  (defun chris/elfeed-bongo-switch-to-playlist ()
    (interactive)
    (let* ((bongo-pl chris/elfeed-bongo-playlist)
           (buffer (get-buffer bongo-pl)))
      (if buffer
          (switch-to-buffer buffer)
        (message "No `bongo' playlist is associated with `elfeed'."))))

  (setq elfeed-search-filter "@6-days-ago +unread")

  (defun chris/elfeed-ui-setup ()
    (display-line-numbers-mode -1)
    (toggle-truncate-lines +1))

  (defun chris/elfeed-show-ui-setup ()
    (display-line-numbers-mode -1)
    (setq visual-fill-column-width 130
          visual-fill-column-center-text t)
    (toggle-truncate-lines -1)
    (visual-fill-column-mode +1))

  (add-hook 'elfeed-search-mode-hook 'chris/elfeed-ui-setup)
  (add-hook 'elfeed-show-mode-hook 'chris/elfeed-show-ui-setup)

  (setq shr-use-colors nil)

  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "of" 'elfeed)

  (general-def 'normal elfeed-search-mode-map
    "v" 'chris/elfeed-bongo-insert-item
    "h" 'chris/elfeed-bongo-switch-to-playlist))
(use-package elfeed-org
  :after elfeed
  :config
  (setq rmh-elfeed-org-files (list "~/org/elfeed.org"))
  (elfeed-org)
  (elfeed-update))

Bongo

(use-package bongo
  :commands (bongo bongo-playlist-buffer)
  :config
  (setq bongo-default-directory "~/Music"
        bongo-prefer-library-buffers nil
        bongo-insert-whole-directory-trees t
        bongo-logo nil
        bongo-display-playback-mode-indicator t
        bongo-display-inline-playback-progress t
        bongo-field-separator (propertize " · " 'face 'shadow))

  (define-bongo-backend mpv
                        :constructor 'bongo-start-mpv-player
                        :program-name 'mpv
                        :extra-program-arguments '("--profile=fast --input-ipc-server=/tmp/mpvsocket")
                        :matcher '((local-file "file:" "http:" "ftp:" "lbry:")
                                   "mpg" "mpeg" "vob" "avi" "ogm" "mp4"
                                   "mkv" "mov" "asf" "wmv" "rm" "rmvb" "ts")
                        :matcher '(("mms:" "mmst:" "rtp:" "rtsp:" "udp:" "unsv:"
                                    "dvd:" "vcd:" "tv:" "dvb:" "mf:" "cdda:" "cddb:"
                                    "cue:" "sdp:" "mpst:" "tivo:") . t)
                        :matcher '(("http:" "https:" "lbry:") . t))

  (define-bongo-backend mpv-music
                        :constructor 'bongo-start-mpv-player
                        :program-name-variable 'mpv
                        :extra-program-arguments '("--profile=slow --input-ipc-server=/tmp/mpvsocket")
                        :matcher '((local-file "file:" "http:" "ftp:" "lbry:")
                                   "mka" "wav" "wma" "ogm" "opus" 
                                   "ogg" "flac" "mp3" "mka" "wav")
                        :matcher '(("http:" "https:" "lbry:") . t))

  (setq bongo-custom-backend-matchers '((mpv-music local-file
                                                   "mka" "wav" "wma" "ogm" "opus" 
                                                   "ogg" "flac" "mp3" "mka" "wav")
                                        (mpv local-file
                                             "mpg" "mpeg" "vob" "avi" "ogm" "mp4"
                                             "mkv" "mov" "asf" "wmv" "rm" "rmvb" "ts")))

  (setq bongo-enabled-backends '(mpv mpv-music)
        bongo-track-mark-icon-file-name "track-mark-icon.png")

  (defun chris/bongo-mark-line-forward ()
    (interactive)
    (bongo-mark-line)
    (goto-char (bongo-point-after-object))
    (next-line))

  (defun chris/bongo-mpv-pause/resume ()
    (interactive)
    (bongo-mpv-player-pause/resume bongo-player))

  (defun chris/bongo-mpv-speed-up ()
    (interactive)
    (bongo--run-mpv-command bongo-player "speed" "set" "speed" "1.95"))

  (defun chris/bongo-open-elfeed-queue-buffer ()
    (interactive)
    (display-buffer "*Bongo-Elfeed Queue*"))

  :general
  (chris/leader-keys
    "ob" 'bongo
    "oB" 'chris/bongo-open-elfeed-queue-buffer
    "mi" 'bongo-insert-enqueue
    "mp" 'bongo-pause/resume)
  (general-def 'normal bongo-playlist-mode-map
    "RET" 'bongo-dwim
    "d" 'bongo-kill-line
    "u" 'bongo-unmark-region
    "p" 'bongo-pause/resume
    "P" 'bongo-yank
    "H" 'bongo-switch-buffers
    "q" 'bury-buffer
    "+" 'chris/bongo-mpv-speed-up
    "m" 'chris/bongo-mark-line-forward)
  (general-def 'normal bongo-library-mode-map
    "RET" 'bongo-dwim
    "d" 'bongo-kill-line
    "u" 'bongo-unmark-region
    "e" 'bongo-insert-enqueue
    "p" 'bongo-pause/resume
    "H" 'bongo-switch-buffers
    "q" 'bury-buffer))

EMMS

Since Bongo seems kinda difficult I shall give EMMS another try.

(use-package emms
  :config
  (emms-all)
  (evil-collection-emms-setup)
  (emms-default-players)
  (setq emms-source-file-default-directory "~/Music/")

  (defun chris/emms-delete-song ()
    "Deletes files in the emms browser by default. Maybe I'll have a yes or no thingy..."
    (interactive)
    (if (yes-or-no-p "delete the file too?")
        (emms-browser-remove-tracks t)
      (emms-browser-remove-tracks)))

  :general
  (chris/leader-keys
    :states 'normal
    :keymaps 'override
    "mo" 'emms
    "mb" 'emms-browser
    "mp" 'emms-pause
    "ma" 'emms-add-dired)
  (general-def 'normal emms-playlist-mode-map
    "q" 'bury-buffer
    "d" 'emms-playlist-mode-kill-track
    "D" 'emms-playlist-mode-goto-dired-at-point)
  (general-def 'normal emms-browser-mode-map
    "q" 'bury-buffer
    "d" 'chris/emms-delete-song
    "D" 'emms-browser-view-in-dired))

Transmission

I use transmission on a server to manage my torrents

(use-package transmission
  :commands (transmission)
  :config

  (if (string-equal (system-name) "archdesktop")
      (setq transmission-host "home.cochrun.xyz"
            transmission-rpc-path "/transmission/rpc"
            transmission-refresh-modes '(transmission-mode
                                         transmission-files-mode
                                         transmission-info-mode
                                         transmission-peers-mode))
    (setq transmission-host "192.168.1.2"
          transmission-rpc-path "/transmission/rpc"
          transmission-refresh-modes '(transmission-mode
                                       transmission-files-mode
                                       transmission-info-mode
                                       transmission-peers-mode)))
    :general
    (chris/leader-keys
      :states 'normal
      :keymaps 'override
      "ot" 'transmission))

Pass

I like to keep my passwords in password-store for a very unixy way of doing things.

(use-package auth-source-pass
  :defer 1
  :config (auth-source-pass-enable))
(use-package pass
  :defer 1)
(use-package password-store
  :after pass
  :general
  (chris/leader-keys
    "sp" 'password-store-copy))
(use-package password-store-otp
  :after password-store
  :general
  (chris/leader-keys
    "st" 'password-store-otp-token-copy))

Matrix/Ement

Matrix.el is a decent enough matrix client built in emacs. Like it.

(use-package plz
  :straight (plz :type git :host github :repo "alphapapa/plz.el"))

(use-package ement
  :straight (ement :type git :host github :repo "alphapapa/ement.el")
  :config
  (setq ement-room-images t
        ement-save-sessions t)
  :general
  (general-def 'normal ement-room-mode-map
    "q" 'bury-buffer
    "RET" 'ement-room-send-message
    "r" 'ement-room-send-reply
    "gr" 'ement-room-sync
    "R" 'ement-room-send-reaction)
  (chris/leader-keys
    "oM" 'ement-list-rooms))

ActivityWatch

I like to track my time with ActivityWatch so I can notice and kill bad habits.

(use-package activity-watch-mode
  :init
  (if (string-equal (system-name) "syl")
      (global-activity-watch-mode -1)
    (global-activity-watch-mode -1)))

MyBible

MyBible is going to be my minor mode for creating and using a bible app within Emacs. Let's see if we can't make it work.

(defun chris/find-translation-tag ()
    (interactive)
  )

Performance

Here we have a bunch of performance tweaks

;; Reduce rendering/line scan work for Emacs by not rendering cursors or regions
;; in non-focused windows.
(setq-default cursor-in-non-selected-windows nil)
(setq highlight-nonselected-windows nil)

;; More performant rapid scrolling over unfontified regions. May cause brief
;; spells of inaccurate syntax highlighting right after scrolling, which should
;; quickly self-correct.
(setq fast-but-imprecise-scrolling t)

;; Don't ping things that look like domain names.
(setq ffap-machine-p-known 'reject)

;; Emacs "updates" its ui more often than it needs to, so we slow it down
;; slightly from 0.5s:
(setq idle-update-delay 1.0)

;; Font compacting can be terribly expensive, especially for rendering icon
;; fonts on Windows. Whether disabling it has a notable affect on Linux and Mac
;; hasn't been determined, but we inhibit it there anyway. This increases memory
;; usage, however!
;; (setq inhibit-compacting-font-caches t)

;; Introduced in Emacs HEAD (b2f8c9f), this inhibits fontification while
;; receiving input, which should help with performance while scrolling.
(setq redisplay-skip-fontification-on-input t)

Garbage Collection

We set the gc-cons-threshold variable to really high, now lets set it back low to make sure emacs performs properly.

(setq gc-cons-threshold (* 32 1024 1024))
(setq garbage-collection-messages nil)

Let's also use an automatic garbage collector while idle to make better input.

(use-package gcmh
  :ensure t
  :init
  (gcmh-mode)
  :config
  (setq gcmh-idle-delay 5
        gcmh-high-cons-threshold (* 128 1024 1024)  ; 128mb
        gcmh-verbose nil))

Logging

Using Emacs 28 there are a lot of comp warnings so I am suppressing those for a quieter compilation.

(setq warning-suppress-types '((comp)))

Early Init

As of right now I haven't fully setup my early-init file, this does not export into the early-init.el file currently as it's just a copy from Doom's early-init.

;;; early-init.el -*- lexical-binding: t; -*-

;; Emacs 27.1 introduced early-init.el, which is run before init.el, before
;; package and UI initialization happens, and before site files are loaded.

;; A big contributor to startup times is garbage collection. We up the gc
;; threshold to temporarily prevent it from running, then reset it later by
;; enabling `gcmh-mode'. Not resetting it will cause stuttering/freezes.
(setq gc-cons-threshold 50000000)
(message "set gc-cons-threshold to 50000000")

;; ;; In noninteractive sessions, prioritize non-byte-compiled source files to
;; ;; prevent the use of stale byte-code. Otherwise, it saves us a little IO time
;; ;; to skip the mtime checks on every *.elc file.
;; (setq load-prefer-newer noninteractive)

;; ;; In Emacs 27+, package initialization occurs before `user-init-file' is
;; ;; loaded, but after `early-init-file'. Doom handles package initialization, so
;; ;; we must prevent Emacs from doing it early!
;; (setq package-enable-at-startup nil)
;; (fset #'package--ensure-init-file #'ignore)  ; DEPRECATED Removed in 28
(setq package-enable-at-startup nil)

;; ;; `file-name-handler-alist' is consulted on every `require', `load' and various
;; ;; path/io functions. You get a minor speed up by nooping this. However, this
;; ;; may cause problems on builds of Emacs where its site lisp files aren't
;; ;; byte-compiled and we're forced to load the *.el.gz files (e.g. on Alpine)
;; (unless (daemonp)
;;   (defvar doom--initial-file-name-handler-alist file-name-handler-alist)
;;   (setq file-name-handler-alist nil)
;;   ;; Restore `file-name-handler-alist' later, because it is needed for handling
;;   ;; encrypted or compressed files, among other things.
;;   (defun doom-reset-file-handler-alist-h ()
;;     ;; Re-add rather than `setq', because changes to `file-name-handler-alist'
;;     ;; since startup ought to be preserved.
;;     (dolist (handler file-name-handler-alist)
;;       (add-to-list 'doom--initial-file-name-handler-alist handler))
;;     (setq file-name-handler-alist doom--initial-file-name-handler-alist))
;;   (add-hook 'emacs-startup-hook #'doom-reset-file-handler-alist-h))

;; ;; Ensure Doom is running out of this file's directory
;; (setq user-emacs-directory (file-name-directory load-file-name))

;; ;; Load the heart of Doom Emacs
;; (load (concat user-emacs-directory "core/core") nil 'nomessage)