My Literate Emacs Configuration

Introduction

This is my literate emacs configuration, which leverages org-mode as a markup language. This allows for prose describing the intention of the various sections of the configuration and code-blocks containing the actual configuration to co-exist in the same plain-text file. One feature of my configuration is that I have the configuration sections tangled out of the .org file into the appropriate Emacs Lisp files every time I save the literate configuration file.

Files related to this configuration:

My Emacs configuration leverages a directory (${HOME}/.emacs.d) that contains both an early-init.el and an init.el file. The majority of the configuration is contained in the Emacs.org file in the same directory.

early-init.el

This file sets the following types of configuration parameters prior to the initialization of the package system and the GUI components.

  1. set the garbage collection threshold to 200 MBs while loading (and processing) the subsequent configuration files.
  2. disable the provided package.el functionality so we can leverage straight.el later.
  3. disable file-name-handlers temporarily
  4. don’t load the site-lisp/init.el file, if present
  5. disable menu-bar-lines, tool-bar-lines, and vertical-scroll-bars for the default-frame-alist
  6. leverage native-comp when available

Here’s the contents of early-init.el:

init.el

This file provides the necessary configuration components to process the main configuration file Emacs.org (this file). It has the following types of configuration settings:

  1. loading required Emacs Lisp ilbrarys (such as cl-lib for the Common Lisp subset implemented for Emacs Lisp).
  2. properly set the values of user-init-file and user-emacs-directory
  3. enable separation of Emacs Customization settings in a separate file (custom.el)
  4. prevent loading of the built-in version of org-mode
  5. configure an alternative mechanism for loading packages by:
    1. setup the necessary filesystem infrastructure to support using straight.el
    2. configure use-package macro to leverage straight.el
    3. load straight.el (via use-package!), including github.com configuration settings
  6. use diminish.el
  7. use org-mode, including org-babel
  8. set the value of config-file to this file (Emacs.org, in the user-emacs-directory)
  9. org-babel-load-file the config-file

Here’s the contents of init.el:

Make sure org-babel generated files are labeled as such

This code block should go in the top of each file generated by org-babel:

;;; Org-mode generated this file from a code block, changes will be overwritten

Startup Performance

I want to know how long it took for startup to happen.

(defun my/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 #'my/display-startup-time)

Separate customization configuration into separate file

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file)

Enable easy configuration reloading

This makes it easy to open the config file:

(defun my/open-config ()
  (interactive)
  (find-file config-file))

This makes it easy to reload the config that has changed:

(defun my/reload-config ()
  "Reload init file, which will effectivley reload everything"
  (interactive)
  (load-file (expand-file-name "init.el" user-emacs-directory)))

Define macro for performing actions based on system-type value

(defmacro with-system (type &rest body)
  "Evaluate BODY if `system-type' equals TYPE."
  (declare (indent defun))
  `(when (eq system-type ',type)
     ,@body))

Package Management

We’re going to use straight.el to provide a deterministic reproducible set of package versions to rely on.

(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))

;; configure some variables
(setq straight-repository-branch "develop" ; Need this for new org-contrib location
      straight-use-package-by-default t
      straight-use-package 'use-package)

Now, we can configure use-package to use straight for package fetching.

(setq use-package-always-ensure nil   ; Make sure this is nil, so we do not use package.el
      use-package-verbose 'debug)      ; TODO  use a debug var for all of config?

;; From this point on we should be able to use `use-package
(use-package straight
  :custom
  (straight-host-usernames '((github . "jonBoone"))) ; TODO Move to personal information?
  ;; Make sure packages do not pull in internal org, we pregister org from straight.el
  (straight-register-package 'org)
  (straight-register-package 'org-contrib))

;; FROM THIS POINT use-package should work as intended, i.e. using straight.

;; Need to install dependencies of use-package manually, why??
(use-package diminish)

Default Coding System

Ensure that the default coding-system is set to UTF-8.

(set-default-coding-systems 'utf-8)

Enable Emacs Server

Start the Emacs server to receive all `emacsclient` calls.

;;(server-start)

Leverage Async Behavior

(use-package async
  :straight t
  :defer t
  :init
  (dired-async-mode 1))

Emacs Lisp Libraries

comment-tags

comment-tags.el adds highlighting and listing capabilities to comment tags

(use-package comment-tags
  :straight t)

dash

dash.el is a modern list library addition to Emacs Lisp

(use-package dash
  :straight t)

ht

ht.el is a hash table library for Emacs Lisp

(use-package ht
  :straight t)

s

s.el is a string manipulation library for Emacs Lisp

(use-package s
  :straight t)

ts

ts.el is a timestamp and date library for Emacs Lisp

(use-package ts
  :straight t)

Keybinding Configuration

`ESC` Cancels All

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

Keybinding Panel `(which-key)`

`which-key` provides an overview of the available keybindings based on the prefix keys entered so far.

(use-package which-key
  :init (which-key-mode)
  :diminish which-key-mode
  :config
  (setq which-key-idle-delay 0.3))

Environment Variable Capture

On some sytems Emacs may be launched without the benefit of the user’s shell initialization files having been evaluated first. So, we need to capture the state of the environment and store it in a file for later reference:

(use-package exec-path-from-shell
  :init
  (setq exec-path-from-shell-check-startup-files nil)
  :config
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))

Manage Popup Windows

(use-package popper
  :straight t
  :disabled
  :bind
  (("M-`"     . popper-toggle-latest)
   ("M-~"     . popper-cycle)
   ("C-x M-`" . popper-toggle-type))
  :config
  (setq popper-reference-buffers
        '("\\*Messages\\*"
          "\\*Warnings\\*"
          "\\*xref\\*"
          "\\*Backtrace\\*"
          "*Flymake diagnostics.*"
          "\\*eldoc\\*"
          "\\*compilation\\*"
          "\\*rustic-"
          "^*tex"
          "\\*Ement Notifications\\*"
          "Output\\*$"
          "\\*Async Shell Command\\*"
          "\\*Dtache Shell Command\\*"
          "\\*GDB.*out\\*"
          help-mode
          compilation-mode)
        popper-display-control 'user)
  (popper-mode +1))

Keep Folders Clean

We use the no-littering package to keep folders where we edit files and the Emacs configuration folder clean! It knows about a wide variety of variables for built in Emacs features as well as those from community packages so it can be much easier than finding and setting these variables yourself.

;; NOTE: If you want to move everything out of the ~/.emacs.d folder
;; reliably, set `user-emacs-directory` before loading no-littering!
                                        ;(setq user-emacs-directory "~/.cache/emacs")
;; NOTE: this is handled via the profiles.el file that is used by chemacs2

(use-package no-littering
  :straight t
  :config
  ;; 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))))

General Configuration

User Interface

This section configures UI settings that remove unneeded elements to make Emacs look a lot more minimal and modern. If you’re just getting started in Emacs, the menu bar might be helpful so you can remove the (menu-bar-mode -1) line if you’d like to still see that.

Visual Configuration

;; This may need to be adjusted from system to system
;; Initialize them to nil
(defvar my/default-font-size nil)
(defvar my/default-variable-font-size nil)

(with-system darwin
  (setq my/default-font-size 160
        my/default-variable-font-size 160))
(with-system gnu/linux
  (setq my/default-font-size 240
        my/default-variable-font-size 240))

;; Make frame transparency overridable
(defvar my/frame-transparency '(90 . 90))

(setq inhibit-startup-message t)

;; change truncation indicators
(define-fringe-bitmap 'right-curly-arrow
  [#b10000000
   #b10000000
   #b01000000
   #b01000000
   #b00100000
   #b00100000
   #b00010000
   #b00010000
   #b00001000
   #b00001000
   #b00000100
   #b00000100])
(define-fringe-bitmap 'left-curly-arrow
  [#b00000100
   #b00000100
   #b00001000
   #b00001000
   #b00010000
   #b00010000
   #b00100000
   #b00100000
   #b01000000
   #b01000000
   #b10000000
   #b10000000])
(set-fringe-mode 10)        ; Give some breathing room

(scroll-bar-mode -1)        ; Disable visible scrollbar
(tool-bar-mode -1)          ; Disable the toolbar
(tooltip-mode -1)           ; Disable tooltips
(menu-bar-mode -1)            ; Disable the menu bar

;; Set up the visible bell
(setq visible-bell t)

;; remove gaps between emacs frames and other windows
(setq frame-resize-pixelwise t)

(column-number-mode)
(global-display-line-numbers-mode t)

;; Don't pop up UI dialogs when prompting
(setq use-dialog-box nil)

;; Remember recently edited files
(recentf-mode 1)

;; remembering minibuffer prompt history
(setq history-length            25
      history-delete-duplicates t)
(savehist-mode 1)

;; Remember and restore the last cursor location of opened files
(save-place-mode 1)

;; Set frame transparency
(set-frame-parameter (selected-frame) 'alpha my/frame-transparency)
(add-to-list 'default-frame-alist `(alpha . ,my/frame-transparency))
;; (set-frame-parameter (selected-frame) 'fullscreen 'maximized)
;; (add-to-list 'default-frame-alist '(fullscreen . maximized))

;; Disable line numbers for some modes
(dolist (mode '(term-mode-hook
                shell-mode-hook
                treemacs-mode-hook
                eshell-mode-hook))
  (add-hook mode (lambda () (display-line-numbers-mode 0))))

(setq large-file-warning-threshold nil      ;; Don't warn regarding large files
      vc-follow-symlinks           t        ;; Always follow symlinked files
      ad-redefinition-action       'accept  ;; add advice wihtout warning
      x-stretch-cursor             t)

Eliminate having to type out `yes` or `no`

(fset 'yes-or-no-p 'y-or-n-p)

Font Configuration

(use-package nerd-icons
  :straight t)
(set-face-attribute 'default nil
                    :font "MesloLGS Nerd Font" :height my/default-font-size)
;; Set the fixed pitch face
(set-face-attribute 'fixed-pitch nil
                    :font "MesloLGS Nerd Font Mono" :height my/default-font-size)
;; Set the variable pitch face
(set-face-attribute 'variable-pitch nil
                    :font "Cantarell" :height my/default-variable-font-size :weight
                    'regular)

Command Log Mode

command-log-mode is useful for displaying a panel showing each key binding you use in a panel on the right side of the frame. Great for live streams and screencasts!

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

Color Theme

doom-themes is a great set of themes with a lot of variety and support for many different Emacs modes. Taking a look at the screenshots might help you decide which one you like best. You can also run M-x counsel-load-theme to choose between them easily.

(use-package doom-themes
  :init
  (load-theme 'doom-nord-light  t)
  (doom-themes-visual-bell-config))

Mode Line

Basic Customization

(setq display-time-format "%l:%M %p %b %y"
      display-time-default-load-average nil)

Smart Mode Line

Prettify the mode line with `smart-mode-line`. Really need to re-evaluate the ordering of `mode-line-format`.

(use-package smart-mode-line
  :config
  (setq sml/no-confirm-load-theme t)
  (sml/setup)
  (sml/apply-theme 'respectful) ; Respect the theme colors
  (setq sml/mode-width 'right
        sml/name-width 60)

  (setq-default mode-line-format
                `("%e"
                  mode-line-front-space
                  mode-line-client
                  mode-line-modified
                  mode-line-remote
                  mode-line-frame-identification
                  mode-line-buffer-identification
                  sml/pos-id-separator
                  (vc-mode vc-mode)
                  " "
                  sml/pre-modes-separator
                  mode-line-modes
                  " "
                  mode-line-misc-info)))

Doom Mode Line

doom-modeline is a very attractive and rich (yet still minimal) mode line configuration for Emacs. The default configuration is quite good but you can check out the configuration options for more things you can enable or disable. NOTE: The first time you load your configuration on a new machine, you’ll need to run `M-x all-the-icons-install-fonts` so that mode line icons display correctly.

(use-package minions
  :hook doom-modeline-mode)

(use-package nerd-icons-ibuffer
  :straight t
  :hook (ibuffer-mode . nerd-icons-ibuffer-mode))

(use-package nerd-icons-completion
  :straight t
  :config
  (nerd-icons-completion-mode))

  (use-package doom-modeline
    :straight t
    :hook (after-init . doom-modeline-mode)
    :custom-face
    (mode-line ((t (:height 0.90))))
    (mode-line-inactive ((t (:height 0.85))))
    :init (doom-modeline-mode 1)
    :config
    (setq doom-modeline-height                 15
          doom-modeline-bar-width              6
          doom-modeline-buffer-file-name-style 'relative-to-project
          doom-modeline-buffer-state-icon      t
          doom-modeline-github                 t
          doom-modeline-hud                    t
          doom-modeline-icon                   t
          doom-modeline-lsp                    t
          doom-modeline-major-mode-color-icon  t
          doom-modeline-major-mode-icon        t
          doom-modeline-minor-mode-icon        t
          doom-modeline-minor-modes            t
          doom-modeline-project-detection      nil
          doom-modeline-unicode-fallback       t))

Encrypt Files

I want to encrypt file swith gpg by default.

(with-system darwin
  (setq epg-gpg-program "gpg"))

(with-system gnu/linux
  (setq epg-gpg-program "gpg2"))

(setq auth-source-debug    t
      auth-sources         '((:source "~/.authinfo.gpg"))
      epa-file-encrypt-to  '("ipmonger@delamancha.org"))

Workspaces

(use-package perspective
  :straight t
  :disabled
  :demand t
  :bind (("C-M-k" . persp-switch)
         ("C-M-n" . persp-next)
         ("C-x k" . persp-kill-buffer*))
  :config
  (setq persp-initial-frame-name "Main")
  (unless (equal persp-mode t)
    (persp-mode)))

Auto-Saving Changed Files

(use-package super-save
  :defer t
  :diminish super-save-mode
  :config
  (super-save-mode +1)
  (setq super-save-auto-save-when-idle t))

Auto-Reverting Changed Files

(setq global-auto-revert-non-file-buffers t) ; Revert Dired and other buffers
(global-auto-revert-mode 1) ; Revert buffers when the underlying file has changed

Highlight Matching Braces

(use-package paren
  :config
  (set-face-attribute 'show-paren-match-expression nil :background "#363e4a")
  (show-paren-mode 1))

Display World Time

`display-time-world` provides a nice display of the time at a specified list of timezones. Nice for working in a team with remote members.

(setq display-time-world-list
      `(("Etc/UTC" "UTC")
        ("America/Los_Angeles" "Silicon Valley")
        ("America/New_York" "South Eastern PA"))
      display-time-world-time-format
      "%a, %d %b %I:%M %p %Z")

TRAMP

(setq tramp-default-method "ssh")

Emacs Dashboard

I like the idea of having emacs launch and present a dashboard as the default buffer in my primary frame. This requires using the `dashboard` package:

(use-package dashboard
  :ensure t
  :config
  (setq
   dashboard-banner-logo-title "My Dashboard"
   dashboard-startup-banner    (concat user-emacs-directory "Emacs-Is-Sexy-V2-64x64.png")
   dashboard-center-content    t
   dashboard-items             '((agenda   . 16))
   dashboard-icon-type         'all-the-icons
   dashboard-display-icons-p   t
   dashboard-icon-type         'nerd-icons)
  (dashboard-setup-startup-hook))

Editing Configuration

Tab Widths

Default to an indentation size of 2 spaces since it’s the norm for nearly all programming languages I use.

(setq-default tab-width 2)

Spaces Instead of Tabs

(setq-default indent-tabs-mode nil)

Spell Check Settings

(use-package flyspell
  :straight t
  :hook (text-mode . flyspell)
  :diminish
  :init
  (add-hook 'prog-mode-hook #'flyspell-prog-mode)
  :config
  (setq ispell-program-name "aspell"   ; use aspell instead of ispell
        ispell-extra-args   '("--sug-mode=ultra")))
(use-package flycheck
  :straight t
  :hook (after-init . global-flycheck)
  :diminish)

Automatically Clean Whitespace

(use-package ws-butler
  :hook (prog-mode text-mode))

Expand Region

This module is absolutely necessary for working inside of Emacs Lisp files, especially when trying to edit some parent of an expression (like a `setq`). Makes tweaking Org agend views much less annoying.

(use-package expand-region
  :bind (("M-[" . er/expand-region)
         ("C-(" . er/mark-outside-pairs)))

Use Parinfer for Lisp Languages

(use-package parinfer
  :hook
  (clojure-mode emacs-lisp-mode common-lisp-mode scheme-mode lisp-mode)
  :config
  (setq parinfer-extensions
        '(defaults         ; should be included
          pretty-parens    ; different paren styles for different modes
          smart-tab        ; C-b & C-f jump positions and smart shift with tab & S-tab
          smart-yank)))    ; Yank behavior depends on mode

Ivy, Counsel and Swiper

Ivy is an excellent completion framework for Emacs. It provides a minimal yet powerful selection menu that appears when you open files, switch buffers, and for many other tasks in Emacs. Counsel is a customized set of commands to replace `find-file` with `counsel-find-file`, etc which provide useful commands for each of the default completion commands. ivy-rich adds extra columns to a few of the Counsel commands to provide more information about each item.

Here are some workflow notes on how to best use Ivy:

While in an Ivy minibuffer, you can search within the current results by using `S-Space`.

To quickly jump to an item in the minibuffer, use `C-‘` to get Avy line jump keys.

To see actions for the selected minibuffer item, use `M-o` then press the action’s key.

**Super useful:** Use `C-c C-o` to open `ivy-occur` to open the search results in a separate buffer. From there you can click any item to perform the Ivy action.

  (use-package ivy
    :diminish
    :bind
    (("C-s" . swiper)
     (:map ivy-minibuffer-map
           ("TAB" . ivy-alt-done)
           ("C-f" . ivy-alt-done)
           ("C-l" . ivy-alt-done)
           ("C-j" . ivy-next-line)
           ("C-k" . ivy-previous-line))
     (:map ivy-switch-buffer-map
           ("C-k" . ivy-previous-line)
           ("C-l" . ivy-done)
           ("C-d" . ivy-switch-buffer-kill))
     (:map ivy-reverse-i-search-map
           ("C-k" . ivy-previous-line)
           ("C-d" . ivy-reverse-i-search-kill)))
    :init
    (ivy-mode 1)
    :config
    (setq ivy-use-virtual-buffers      t
          ivy-wrap                     t
          ivy-count-format             "(%d/%d) "
          enable-recursive-minibuffers t)

    ;; Use different regex strategies per completion command
    (push '(completion-at-point . ivy--regex-fuzzy) ivy-re-builders-alist) ;; This doesn't seem to work...
    (push '(swiper . ivy--regex-ignore-order) ivy-re-builders-alist)
    (push '(counsel-M-x . ivy--regex-ignore-order) ivy-re-builders-alist)

    ;; Set minibuffer height for different commands
    (setf (alist-get 'counsel-projectile-ag ivy-height-alist) 15
          (alist-get 'counsel-projectile-rg ivy-height-alist) 15
          (alist-get 'swiper ivy-height-alist) 15
          (alist-get 'counsel-switch-buffer ivy-height-alist) 7))

  (use-package ivy-rich
    :init
    (ivy-rich-mode 1)
    :after counsel
    :config
    (setq ivy-format-function #'ivy-format-function-line
          ivy-rich-display-transformers-list
          (plist-put ivy-rich-display-transformers-list
                     'ivy-switch-buffer
                     '(:columns
                       ((ivy-rich-candidate (:width 40))
                        (ivy-rich-switch-buffer-indicators
                         (:width 4 :face error :align right)) ; return the buffer indicators
                        (ivy-rich-switch-buffer-major-mode
                         (:width 12 :face warning))           ; return the major mode info
                        (ivy-rich-switch-buffer-project
                         (:width 15 :face success))           ; return project name using `projectile'
                        (ivy-rich-switch-buffer-path
                         (:width (lambda (x)                  ; return file path relative to project root or `default-directory' if project is nil
                                   (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))))))

(use-package nerd-icons-ivy-rich
  :after ivy-rich
  :straight t
  :init
  (nerd-icons-ivy-rich-mode 1)
  (ivy-rich-mode 1))

  (use-package counsel
    :demand t
    :bind
    (("M-x" . counsel-M-x)
     ("C-x b" . counsel-ibuffer)
     ("C-x C-f" . counsel-find-file) ; ("C-M-j" . counsel-switch-buffer)
     ("C-M-l" . counsel-imenu)
     ("C-M-j" . counsel-switch-buffer)
     (:map minibuffer-local-map
           ("C-r" . 'counsel-minibuffer-history)))
    :config
    (setq counsel-linux-app-format-function
          #'counsel-linux-app-format-function-name-only
          ivy-initial-inputs-alist nil)) ; Don't start searches with ^


  (use-package flx  ; Improves sorting for fuzzy-matched results
    :after ivy
    :defer t
    :init
    (setq ivy-flx-limit 10000))


  (use-package wgrep)

  (use-package ivy-posframe
    :disabled
    :config
    (setq ivy-posframe-width      115
          ivy-posframe-min-width  115
          ivy-posframe-height     10
          ivy-posframe-min-height 10
          ivy-posframe-display-functions-alist
          '((t . ivy-posframe-display-at-frame-center))
          ivy-posframe-parameters
          '((parent-frame . nil)
            (left-fringe . 8)
            (right-fringe . 8)))
    (ivy-posframe-mode 1))

  (use-package prescient
    :after counsel
    :config
    (prescient-persist-mode 1))

  (use-package ivy-prescient
    :after prescient
    :config
    (ivy-prescient-mode 1))

Jumping with Avy

(use-package avy
  :commands (avy-goto-char avy-goto-word-0 avy-goto-line))

Window Management

Frame Scaling / Zooming

The keybindings for this are `C+M+-` and `C+M+=`.

(use-package default-text-scale
  :defer t
  :config
  (default-text-scale-mode))

Window History with `winner-mode`

(use-package winner
  :straight nil
  :bind
  (("s-/" . winner-undo)
   ("s-?" . winner-redo))
  :config
  (winner-mode))

Set Margins for Modes

(defun my/center-buffer-with-margins ()
  (let ((margin-size (/ (- (frame-width) 110) 3)))
    (set-window-margins nil margin-size margin-size)))

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

(use-package visual-fill-column
  :defer t
  :hook (org-mode . my/org-mode-visual-fill))

Control Buffer Placement

Emacs’ default buffer placement algorithm is pretty disruptive if you like setting up window layouts a certain way in your workflow. The display-buffer-alist video controls this behavior and you can customize it to prevent Emacs from popping up new windows when you run commands.

(setq display-buffer-base-action
      '(display-buffer-reuse-mode-window
        display-buffer-reuse-window
        display-buffer-same-window))

;; If a popup does happen, don't resize windows to be equal-sized
(setq even-window-sizes nil)

File Browsing

Dired

(use-package nerd-icons-dired
  :straight t
  :hook (dired-mode . nerd-icons-dired-mode))

(use-package dired
  :straight nil
  :commands (dired dired-jump)
  :hook
  (dired-mode . (lambda ()
                  (interactive)
                  ;;(dired-omit-mode 1)
                  ;;(dired-hide-details-mode 1)
                  (hl-line-mode 1)))
  :config
  (require 'dired-x)
  (with-system darwin
    ;; assumes homebrew bin path is in ${PATH}
    (setq dired-use-ls-dired                    t
          insert-directory-program              "gls"))
  (setq dired-listing-switches                  "-alghoA --group-directories-first"
        dired-dwim-target                       t
        dired-deletion-confirmer                'y-or-n-p
        dired-hide-details-hide-symlink-targets nil))

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

(use-package dired-single
  :defer t)

(use-package dired-ranger
  :defer t)

Org Mode

Org Mode is one of the hallmark features of Emacs. It is a rich document editor, project planner, task and time tracker, blogging engine, and literate coding utility all wrapped up in one package.

Essential Org Mode Config

This section contains the basic configuration for org-mode plus the configuration for Org agendas and capture templates.

Support for Better Font Faces

The my/org-font-setup function configures various text faces to tweak the sizes of headings and use variable width fonts in most cases so that it looks more like we’re editing a document in org-mode. We switch back to fixed width (monospace) fonts for code blocks and tables so that they display correctly. Note: This must appear prior to the `(use-package org-mode…)` invocation so that we can call `my/org-font-setup` during the call to `use-package`.

(defun my/org-font-setup ()
  ;; Replace list hyphen with dot
  (font-lock-add-keywords 'org-mode
                          '(("^ *\\([-]\\) "
                             (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))

  ;; Set faces for heading levels
  (dolist (face '((org-level-1 1.35 "#000706")
                  (org-level-2 1.30 "#5a85a9")
                  (org-level-3 1.25 "#455765")
                  (org-level-4 1.20 "#bfbbcb")
                  (org-level-5 1.15 "#b4bbcb")
                  (org-level-6 1.10 "#b1cfeb")
                  (org-level-7 1.05 "#9db6d3")
                  (org-level-8 1.00 "#90a295")))
    (set-face-attribute (car face) nil :font "Cantarell"
                        :weight 'regular :height (cadr face)
                        :foreground (caddr face)))

  ;; Ensure that anything that should be fixed-pitch in Org files appears that way
  (set-face-attribute 'org-block nil    :foreground 'unspecified :inherit 'fixed-pitch)
  (set-face-attribute 'org-table nil    :inherit 'fixed-pitch)
  (set-face-attribute 'org-formula nil  :inherit 'fixed-pitch)
  (set-face-attribute 'org-code nil     :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-table nil    :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-special-keyword nil :background 'unspecified :inherit
                      '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-checkbox nil  :inherit 'fixed-pitch)
  (set-face-attribute 'line-number nil :inherit 'fixed-pitch)
  (set-face-attribute 'line-number-current-line nil :inherit 'fixed-pitch)
  (setq org-agenda-deadline-faces
        '((1.0001 . org-warning)              ; late -- due yesterday or before
          (0.0    . org-upcoming-deadline)))  ; due today or in the future
  )

Org-Mode Agenda Style Configuration

I want to customize my org-mode agenda style.

  (defun my/style-org-agenda ()
<<<<<<< HEAD
    ;;(my/buffer-face-mode-variable)
=======
>>>>>>> 67af178 (delete/comment-out troubleshome elisp expressions)
    (set-face-attribute 'org-agenda-date nil :height 1.1)
    (set-face-attribute 'org-agenda-date-today nil :height 1.1 :slant 'italic)
    (set-face-attribute 'org-agenda-date-weekend nil :height 1.1))

Custom org-mode-setup function

;; Set a custom configuration for org-mode interaction with the other elisp packages.
(defun my/org-mode-setup ()
  (toggle-truncate-lines -1)
  (auto-fill-mode 1)
  (variable-pitch-mode 1)
  (visual-line-mode 1)
  (flyspell-mode 1)
  (diminish org-indent-mode))

Custom org-mode agenda diary fancy cleanup

(defun my/org-agenda-cleanup-fancy-diary ()
  (lambda ()
    (goto-char (point-min))
    (save-excursion
      (while (re-search-forward "^[a-z]" nil t)
        (goto-char (match-beginning 0))
        (insert "00:00-24:00 ")))
    (while (re-search-forward "^ [a-z]" nil t)
      (goto-char (match-beginning 0))
      (save-excursion
        (re-search-backward "^[0-9]+[0-9]+:[0-9]+:[0-9]+ " nil t))
      (insert (match-string 0)))))

Org-Mode package

The configuration of Org-Mode is augmented with some additional modules (see `(setq org-modules …)` line below.)

(use-package org
  :defer t
  :hook ((org-mode                       . my/org-mode-setup)
         (org-agenda-mode                . my/style-org-agenda)
         (org-agenda-cleanup-fancy-diary . my/org-agenda-cleanup-fancy-diary)
         (org-babel-after-execute        . org-redisplay-inline-images))
  :config
  (setq org-adapt-indentation              t
        org-agenda-block-separator         ""
        org-agenda-breadcrumbs-separator   ""
        org-agenda-category-icon-alist     '(("Work" ,(list (nerd-icons-faicon "cogs"))
                                              nil nil :ascent center)
                                             ("Personal" ,(list (nerd-icons-mdicons
                                                                 "person"))
                                              nil nil :ascnet center)
                                             ("Calendar" ,(list (nerd-icons-faicon
                                                                 "calendar"))
                                              nil nil :ascent center)
                                             ("Reading" ,(list (nerd-icons-faicon
                                                                "book"))
                                              nil nil :ascent center))
        org-agenda-clockreport-parameter-plist '(:link t :maxlevel 5 :fileskip0 t
                                                       :compact t :narrow 80)
        org-agenda-compact-blocks          t
        org-agenda-current-time-string     "⏰ ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ now"
        org-agenda-files                   (directory-files-recursively
                                            "~/Dropbox/pkb/org" "\.org$")
        org-agenda-format-date             (lambda (date)
                                             (concat "\n"
                                                     (make-string (window-width) 9472)
                                                     "\n"
                                                     (org-agenda-format-date-aligned
                                                      date)))
        org-agenda-include-deadlines       t
        org-agenda-include-diary           t
        org-agenda-prefix-format           '((agenda . "%i %-12:c%?-12t%b% s")
                                             (todo   . "%i %-12:c")
                                             (tags   . "%i %-12:c")
                                             (search . " %i %-12:c"))
        org-agenda-skip-deadline-if-done   t
        org-agenda-skip-scheduled-if-done  t
        org-agenda-skip-timestamp-if-done  t
        org-agenda-skip-unavailable-files  t
        org-agenda-start-on-weekday        nil ; starts on current day
        org-agenda-start-with-log-mode     t
        org-agenda-time-grid               '((weekly today require-timed)
                                             (0800 1000 1200 1400 1600 1800 2000)
                                             "---" "┈┈┈┈┈┈┈┈┈┈┈┈┈┈")
        org-agenda-todo-ignore-with-date   t
        org-agenda-window-setup            'current-window
        org-cite-global-bibliography       '("~/Dropbox/pkb/org/bib.bib")
        org-cycle-separator-lines          2
        org-ellipsis                       ""
        org-edit-src-content-indentation   2
        org-fontify-done-headline          t
        org-fontify-quote-and-verse-blocks t
        org-fontify-whole-heading-line     t
        org-format-latex-options           (plist-put org-format-latex-options :scale 1.5)
        org-habit-graph-column             60
        org-hide-block-startup             nil
        org-hide-emphasis-markers          t
        org-hide-leading-stars             t
        org-highlight-latex-and-related    '(native)
        org-html-postamble                 t
        org-html-postamble-format          '(("en" "<p class=\"author\">Author: %a (%e)</p>\n<p class=\"date\">Date: %T</p>\n<p class=\"creator\">%c</p>"))
        org-icalendar-alarm-time           30
        org-icalendar-combined-name        "Org Mode Agenda"
        org-icalendar-include-todo         t
        org-icalendar-store-UID            t
        org-icalendar-timezome             "US/New_York"
        org-icalendar-use-deadline         '(todo-due event-if-todo event-if-not-todo)
        org-icalendar-use-scheduled        '(todo-start event-if-todo event-if-not-todo)
        org-latex-create-formula-image-program 'dvisvgm
        org-latex-default-class            "tuffte-handout"
        org-log-done                       'time
        org-log-into-drawer                t
        org-modules                        '(org-crypt
                                             org-irc
                                             org-habit
                                             org-mac-iCal
                                             org-magit
                                             org-notify
                                             org-toc)
        org-outline-path-complete-in-steps nil
        org-plantuml-jar-path              (expand-file-name "/opt/homebrew/Cellar/plantuml/1.2023.11/libexec/plantuml.jar")
        org-pretty-entities                t
        org-refile-targets                 '(("future.org"  :maxlevel . 3)
                                             ("gtd.org"     :maxlevel . 3)
                                             ("in-progress" :maxlevel . 3))
        org-refile-use-outline-path        t
        org-return-follows-link            t
        org-src-fontify-natively           t
        org-src-tab-acts-natively          t
        org-startup-indented               t
        org-startup-folded                 'overview
        org-tag-alist                      '((:startgroup)
                                             ;; Put mutually exclusive tags here
                                             (:endgroup)
                                             ("@errand" . ?E)
                                             ("@home" . ?H)
                                             ("@work" . ?W)
                                             ("agenda" . ?a)
                                             ("planning" . ?p)
                                             ("publish" . ?P)
                                             ("batch" . ?b)
                                             ("note" . ?n)
                                             ("idea" . ?i))
        org-todo-keywords                  '((sequence
                                              "FUTURE(f)" "ACTIVE(a)"
                                              "|" "COMPLETED(c!)" "CANCELLED(k@)"
                                              "DEFERRED(f)")
                                             (sequence "MEET(m)" "|" "MET(M)"))
        org-agenda-custom-commands    '(("d" "Dashboard"
                                         ((agenda "" ((org-deadline-warning-days 7)))))
                                        ("w" "Workflow Status"
                                         ((todo "FUTURE"
                                                ((org-agenda-overriding-header
                                                  "To be handled in the future")
                                                 (org-agenda-files org-agenda-files)))
                                          (todo "ACTIVE"
                                                ((org-agenda-overriding-header
                                                  "Active Projects")
                                                 (org-agenda-files org-agenda-files)))
                                          (todo "COMPLETED"
                                                ((org-agenda-overriding-header
                                                  "Completed Projects")
                                                 (org-agenda-files org-agenda-files)))
                                          (todo "CANCELLED"
                                                ((org-agenda-overriding-header
                                                  "Cancelled Projects")
                                                 (org-agenda-files org-agenda-files)))
                                          (todo "DEFERRED"
                                                ((org-agenda-overriding-header
                                                  "Deferred Projects")
                                                 (org-agenda-files org-agenda-files)))))
                                        ("I" "Import diary from iCloud Calendar"
                                         ((agenda "" ((org-agenda-mode-hook
                                                       (lambda ()
                                                         (org-mac-iCal))))))))
        org-capture-templates         `(("t" "Tasks / Projects")
                                        ("tt" "Task"
                                         entry
                                         (file+olp
                                          "~/Dropbox/pkb/org/GTD.org"
                                          "Inbox")
                                         "* TODO %^{Description}\nAdded:\n%t\n%?\n\n%U\n%a\n%i"
                                         :empty-lines 1)
                                        ("j" "Journal Entries")
                                        ("jj" "Journal"
                                         entry
                                         (file+olp+datetree
                                          "~/Dropbox/pkb/org/Journal.org")
                                         "\n* %<%I:%M %p> - Journal :journal:\n\n%?\n\n"
                                         "~/Notes/Templates/Daily.org"
                                         :clock-in :clock-resume
                                         :empty-lines 1)
                                        ("jm" "Meeting"
                                         entry
                                         (file+olp+datetree
                                          "~/Dropbox/pkb/org/Journal.org")
                                         "* %<%I:%M %p> - %a :meetings:\n\n%?\n\n"
                                         :clock-in :clock-resume
                                         :empty-lines 1)))
  :bind
  ((:map org-mode-map
         (("C-c <up>"      . org-priority-up)
          ("C-c <down>"    . org-priority-down)
          ("C-c C-g C-r"   . org-shiftmetaright)))
   (:map global-map
         (("C-c l"         . org-store-link)
          ("C-c a"         . org-agenda)
          ("C-c c"         . org-capture)
          ("C-c j"         . (lambda ()
                               (interactive)
                               (org-caputre nil "jj")))))))

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

Extra Packages that extend Org-Mode

org-appear - Auto-show Markup Symbols

This package makes it much easier to edit Org documents when `org-hide-emphasis-markers` is turned on. It temporarily shows the emphasis markers around certain markup elements when you place your cursor inside of them. No more fumbling around with `=` and `*` characters!

(use-package org-appear
  :hook org-mode)

org-bullets - Nicer Heading Bullets

org-bullets replaces the heading stars in org-mode buffers with nicer looking characters that you can control. Another option for this is org-superstar-mode which we may cover in a later video.

(use-package org-bullets
  :hook org-mode
  :custom
  (org-bullets-bullet-list '("" "" "" "" "" "" "")))

org-journal

Configure the daily journal features

(use-package org-journal
  :straight t
  :bind (:map org-journal-mode-map
              ("C-<tab>" . yas-expand)))

org-make-toc - Update Table of Contents on Save

It’s nice to have a table of contents section for long literate configuration files (like this one!) so use `org-make-toc` to automatically update the `ToC` in any header with a property named `TOC`.

(use-package org-make-toc
   :hook org-mode)

org-modern interface

(use-package org-modern
  :after org
  :straight t
  :hook
  (org-mode . global-org-modern-mode)
  :config
  (setq org-modern-keyword     t
        org-modern-checkbox    t
        org-modern-block-name  t
        org-modern-table       t))

org-super-agenda

org-super-agenda adds additional functionality to daily/weekly agenda through grouping

(use-package org-super-agenda
  :straight t
  :hook org-agenda-mode)

org-tempo - Structure Templates

Org Mode’s structure templates feature enables you to quickly insert code blocks into your Org files in combination with org-tempo by typing < followed by the template name like el or py and then press TAB. For example, to insert an empty emacs-lisp block below, you can type <el and press TAB to expand into such a block.

You can add more src block templates below by copying one of the lines and changing the two strings at the end, the first to be the template name and the second to contain the name of the language as it is known by Org Babel.

(with-eval-after-load 'org
  ;; This is needed as of Org 9.2
  (require 'org-tempo)

  (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
  (add-to-list 'org-structure-template-alist '("py" . "src python")))

Center Org Buffers

We use visual-fill-column to center org-mode buffers for a more pleasing writing experience as it centers the contents of the buffer horizontally to seem more like you are editing a document. This is really a matter of personal preference so you can remove the block below if you don’t like the behavior.

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

(use-package visual-fill-column
  :hook (org-mode . my/org-mode-visual-fill))

Configure Babel Languages

To execute or export code in org-mode code blocks, you’ll need to set up org-babel-load-languages for each language you’d like to use. This page documents all of the languages that you can use with org-babel.

(with-eval-after-load 'org
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((dot        . t)
     (emacs-lisp . t)
     (latex      . t)
     (plantuml   . t)
     (python     . t)
     (shell      . t)))
  (mapcar #'(lambda (element)
              (push element org-src-lang-modes))
          '(("conf-unix"  . conf-unix)
            ("plantuml"   . platnuml)
            ("python"     . python)
            ("shell"      . shell))))

Pomodoro

(use-package org-pomodoro
  :after org
  :config
  (setq org-pomodoro-start-sound        "~/Dropbox/pkb/sounds/focus_bell.wav"
        org-pomodoro-short-break-sound  "~/Dropbox/pkb/sounds/three_beeps.wav"
        org-pomodoro-long-break-sound   "~/Dropbox/pkb/sounds/three_beeps.wav"
        org-pomodoro-finished-sound     "~/Dropbox/pkb/sounds/meditation_bell.env"))

Export LaTeX Source Blocks

ox-latex is the go-to package here

;;  (use-package ox-latex
;;    :straight t
;;    :defer t
;;    :config
;;    (setq org-latex-listings 'minted
;;          org-latex-pdf-process
;;          '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
;;            "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
;;            "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
;;    (add-to-list 'org-latex-packages-alist '("" "minted"))
;;    (add-to-list 'org-latex-classes
;;                 '("tufte-handout"
;;                   "\\documentclass{tufte-handout}"
;;                   ("\\section{%s}" . "\\section*{%s}")
;;                   ("\\subsection{%s}" . "\\subsection*{%s}")
;;                   ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
;;                   ("\\paragraph{%s}" . "\\paragraph*{%s}")
;;                   ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))

Auto-tangle Configuration Files

This snippet adds a hook to org-mode buffers so that my/org-babel-tangle-config gets executed each time such a buffer gets saved. This function checks to see if the file being saved is the Emacs.org file you’re looking at right now, and if so, automatically exports the configuration here to the associated output files.

;; Automatically tangle our Emacs.org config file when we save it
(defun my/org-babel-tangle-config ()
  (when (string-equal (file-name-directory (buffer-file-name))
                      (expand-file-name user-emacs-directory))
    ;; Dynamic scoping to the rescue
    (let ((org-confirm-babel-evaluate nil))
      (org-babel-tangle))))

(add-hook 'org-mode-hook
          (lambda () (add-hook 'after-save-hook #'my/org-babel-tangle-config)))

Org Roam

(use-package org-roam
  :straight t
  ;; :hook after-init
  :config
  (setq org-roam-directory                  "~/Dropbox/pkb/org/"
        org-roam-completion-everywhere      t
        org-roam-completion-system          'default
        org-roam-capture-templates          '(("d" "default" plain
                                               #'org-roam-capture--get-point
                                               "%?"
                                               :file-name "%<%Y%m%d%H%M%S>-${slug}"
                                               :head "#+title: ${title}\n"
                                               :unnarrowed t)
                                              ("ll" "link note" plain
                                               #'org-roam-capture--get-point
                                               "* %^{Link}"
                                               :file-name "Inbox"
                                               :olp ("Links")
                                               :unnarrowed t
                                               :immediate-finish)
                                              ("lt" "link task" entry
                                               #'org-roam-capture--get-point
                                               "* TODO %^{Link}"
                                               :file-name "Inbox"
                                               :olp ("Tasks")
                                               :unnarrowed t
                                               :immediate-finish))
        org-roam-dailies-directory          "journal/"
        org-roam-dailies-capture-templates  '(("d" "default" entry
                                               #'org-roam-capture--get-point
                                               "* %?"
                                               :file-name "Journal/%<%Y-%m-%d>"
                                               :head "#+title: %<%Y-%m-%d %a>\n\n[[roam:%<%Y-%B>]]\n\n")
                                              ("t" "Task" entry
                                               #'org-roam-capture--get-point
                                               "* TODO %?\n  %U\n  %a\n  %i"
                                               :file-name "Journal/%<%Y-%m-%d>"
                                               :olp ("Tasks")
                                               :empty-lines 1
                                               :head "#+title: %<%Y-%m-%d %a>\n\n[[roam:%<%Y-%B>]]\n\n")
                                              ("j" "journal" entry
                                               #'org-roam-capture--get-point
                                               "* %<%I:%M %p> - Journal  :journal:\n\n%?\n\n"
                                               :file-name "Journal/%<%Y-%m-%d>"
                                               :olp ("Log")
                                               :head "#+title: %<%Y-%m-%d %a>\n\n[[roam:%<%Y-%B>]]\n\n")
                                              ("l" "log entry" entry
                                               #'org-roam-capture--get-point
                                               "* %<%I:%M %p> - %?"
                                               :file-name "Journal/%<%Y-%m-%d>"
                                               :olp ("Log")
                                               :head "#+title: %<%Y-%m-%d %a>\n\n[[roam:%<%Y-%B>]]\n\n")
                                              ("m" "meeting" entry
                                               #'org-roam-capture--get-point
                                               "* %<%I:%M %p> - %^{Meeting Title}  :meetings:\n\n%?\n\n"
                                               :file-name "Journal/%<%Y-%m-%d>"
                                               :olp ("Log")
                                               :head "#+title: %<%Y-%m-%d %a>\n\n[[roam:%<%Y-%B>]]\n\n")))
  ;; ensure we autosync on save
  (org-roam-db-autosync-mode)
  (org-roam-setup)
  :bind
  ((:map org-roam-mode-map
         (("C-c n l"   . org-roam)
          ("C-c n f"   . org-roam-find-file)
          ("C-c n d"   . org-roam-dailies-find-date)
          ("C-c n c"   . org-roam-dailies-capture-today)
          ("C-c n C r" . org-roam-dailies-capture-tomorrow)
          ("C-c n t"   . org-roam-dailies-find-today)
          ("C-c n y"   . org-roam-dailies-find-yesterday)
          ("C-c n r"   . org-roam-dailies-find-tomorrow)
          ("C-c n g"   . org-roam-graph)))
   (:map org-mode-map
         (("C-c n i" . org-roam-insert)
          ("C-c n I" . org-roam-insert-immediate)))))

Deft

(use-package deft
  :commands (deft)
  :config
  (setq deft-directory  "~/Dropbox/pkb/org"
        deft-recursive  t
        deft-extensions '("md" "org")))

Tree-sitter

(use-package tree-sitter
  :straight t)

(use-package tree-sitter-langs
  :straight t
  :after tree-sitter)

LLMs

ChatGPT

;;  (use-package c3po
;;    :straight (:host github :repo "d1egoaz/c3po.el")
;;    :config
;;    (setq c3po-api-key custom-c3po-api-key))

Web Browsing

I build my emacsen with xwidget-webkit support on darwin systems, so I want that to be the default browser function.

;; configure xwidget-webkit-browse-url as default browser function
(setq browse-url-browser-function 'xwidget-webkit-browse-url)

Writing

Markdown Support

Markdown Mode

(use-package markdown-mode
  :straight t
  :mode
  ("README\\.md\\'" . gfm-mode)
  :config
  (setq markdown-command '("pandoc" "-f" "gfm" "-t" "html5")
        markdown-live-preview-window-function #'xwidget-webkit-browse-url))

Metrics

This section comes from Sacha Chua’s blog post Outline Your Notes With Org, part of a series of posts that were expected to be part of a book Wicked Cool Emacs published by No Starch Press.

This section tracks the number of words in a buffer.

(defvar count-words-buffer nil
  "*Number of words in the buffer.")

(defun my/update-wc ()
  (interactive)
  (setq count-words-buffer (number-to-string (my/count-words-buffer)))
  (force-mode-line-update))

;; ensure counter is initialized only once
(unless count-words-buffer
  ;; seed count-words-paragraph
  ;; create timer to keep count-words-paragraph updated
  (run-with-idle-timer 1 t 'my/update-wc))

;; add count words paragraph to the mode line
(unless (memq 'count-words-buffer global-mode-string)
  (add-to-list 'global-mode-string "words: " t)
  (add-to-list 'global-mode-string 'my/count-words-buffer t))

;; count number of words in currrent paragraph
(defun my/count-words-buffer ()
  "Count the number of words in the current paragraph."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((count 0))
      (while (not (eobp))
        (forward-word 1)
        (setq count (1+ count)))
      count)))

This section extends the standard checklist feature in Org, which can track percentage completion of checklist items, to work with headlines as well.

;; (defun my/org-update-checkbox-count (&optional all)
;;   "Update the checkbox statistics in the current section.
;; This will find and update all statistic cookies like [57%] and [6/12] with
;; the current values. If the optional prefix argument all is provided, the
;; entire buffer is examined and the statistics are updated."
;;   (interactive "P")
;;   (save-excursion
;;     (let* ((buffer-invisibility-spec (org-inhibit-invisibility))
;;            (beg (condition-case nil
;;                     (progn (outline-back-to-heading) (point))
;;                   (error (point-min))))
;;            (end (move-marker
;;                  (make-marker)
;;                  (progn (or (outline-get-next-sibling) ;; (1)
;;                             (goto-char (point-max)))
;;                         (point))))
;;            (re "\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*\\]\\)")
;;            (re-box "^[ \t]*\\(*+\\|[-+*]\\|[0-9]+[.)]\\) +\\(\\[[- X]\\]\\)")
;;            b1 e1 f1 c-on c-off lim (cstat 0))
;;       (when all
;;         (goto-char (point-min))
;;         (or (outline-get-next-sibling) (goto-char (point-max))) ;; (2)
;;         (setq beg (point) end (point-max)))
;;       (goto-char beg)
;;       (while (re-search-forward re end t)
;;         (setq cstat (1+ cstat)
;;               b1 (match-begining 0)
;;               e1 (match-end 0)
;;               f1 (match-beginning 1)
;;               lim (cond
;;                    ((org-on-heading-p)
;;                     (or (outline-get-next-sibling) ;; (3)
;;                         (goto-char (point-max)))
;;                     (point))
;;                    ((org-at-item-p) (org-end-of-item) (point))
;;                    (t nil))
;;               c-on 0
;;               c-off 0)
;;         (goto-char e1)
;;         (when lim
;;           (while (re-search-forward re-box lim t)
;;             (if (number (match-string 2) '("[ ]" "[-]"))
;;                 (setq c-off (1+ c-off))
;;               (setq c-on (1+ c-on))))
;;           (goto-char b1)
;;           (insert (if f1
;;                       (format "[%d%%]" (/ (* 100 c-on)
;;                                           (max 1 (+ c-on c-off))))
;;                     (format "[%d/%d]" c-on (+ c-on c-off))))
;;           (and (looking-at "\\[.*?\\]")
;;                (replace-match ""))))
;;       (when (interactive-p)
;;         (message "Checkbox statistics updated %s (%d places)"
;;                  (if all "in entire file" "in current outline entry")
;;                  cstat)))))

;; (defadvice org-update-checkbox-count (around my activate)
;;   "Fix the built-in checkbox count to understand headlines."
;;   (setq ad-return-value
;;         (my/org-update-checkbox-count (ad-get-arg 1))))

Latex

Most folks use Auctex for editing LaTeX in Emacs.

(use-package auctex
    :defer    t
    :straight t
    :hook     (Tex-mode . #'turn-on-reftex)
    :init
    (progn
      (setq TeX-auto-save                  t
            TeX-parse-self                 t
            TeX-PDF-mode                   t
            LaTeX-fill-break-at-separators nil
            TeX-view-program-list          '(("Preview.app" "open -a Preview.app %o")
                                             ("displayline" "displayline -g -b %n %o %b")
                                             ("open" "open %o"))
            TeX-veiw-program-selection     '((output-dvi  "open")
                                             (output-pdf  "Preview.app")
                                             (output-html "open"))))
    :config
    (company-auctex-init))

Development

Magit

Magit is the best Git interface I’ve ever used. Common Git operations are easy to execute quickly using Magit’s command panel system.

(use-package magit
  :straight t
  :bind
  (("C-M-;" . magit-status)
   (:map project-prefix-map
         ("m" . project-magit)))
  :commands (magit-status magit-get-current-branch project-magit)
  :custom
  (add-to-list 'project-switch-commands
               '(project-magit "Magit" m))
  (defun project-magit ()
    (interactive)
    (let ((dir (project-root (project-current t))))
      (magit-status dir)))

  (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1))

;; NOTE: Make sure to configure a GitHub token before using this package!
;; - https://magit.vc/manual/forge/Token-Creation.html#Token-Creation
;; - https://magit.vc/manual/ghub/Getting-Started.html#Getting-Started
(use-package forge
  :straight t
  :after magit)

(use-package magit-todos
  :straight t
  :defer t)

(use-package git-link
  :commands git-link
  :config
  (setq git-link-open-in-browser-t))
(use-package ediff
  :after (magit vc)
  :commands (ediff)
  :init
  (with-eval-after-load 'winner
    (add-hook 'edif-quit-hook 'winner-undo))
  (setq ediff-window-setup-function 'ediff-setup-windows-plain))
;;  (use-package diff-hl
;;    :straight t
;;    :defer 5
;;    :hook ((magit-pre-refresh . diff-hl-magit-pre-refresh)
;;           (magit-pre-refresh . diff-hl-magit-post-refresh))
;;    :init (global-diff-hl-mode)
;;    :config (diff-hl-flydiff-mode))

Projectile

Projectile is a project management library for Emacs which makes it a lot easier to navigate around code projects for various languages. Many packages integrate with Projectile so it’s a good idea to have it installed even if you don’t use its commands directly.

(use-package projectile
  :diminish projectile-mode
  :config (projectile-mode)
  :custom ((projectile-completion-system 'ivy))
  :bind-keymap
  ("C-c p" . projectile-command-map)
  :init
  ;; NOTE: Set this to the folder where you keep your Git repos!
  (when (file-directory-p "~/Projects/Code")
    (setq projectile-project-search-path '("~/Projects/Code")))
  (setq projectile-switch-project-action #'projectile-dired))

(use-package counsel-projectile
  :after projectile
  :config (counsel-projectile-mode))

Programming Language Support

Colorful Delimiters

rainbow-delimiters is useful in programming modes because it colorizes nested parentheses and brackets according to their nesting depth. This makes it a lot easier to visually match parentheses in Emacs Lisp code without having to count them yourself.

(use-package rainbow-delimiters
  :straight t
  :hook (prog-mode . rainbow-delimiters-mode))

Company Mode

Company Mode provides a nicer in-buffer completion interface than completion-at-point which is more reminiscent of what you would expect from an IDE. We add a simple configuration to make the keybindings a little more useful (TAB now completes the selection and initiates completion at the current location if needed).

We also use company-box to further enhance the look of the completions with icons and better overall presentation.

(use-package company
  :after lsp-mode
  :hook (lsp-mode . company-mode)
  :bind
  ((:map company-active-map
         ("<tab>" . company-complete-selection))
   (:map lsp-mode-map
         ("<tab>" . company-indent-or-complete-common)))
  :custom
  (company-minimum-prefix-length 1)
  (company-idle-delay 0.0))

(use-package company-box
  :hook (company-mode . company-box-mode))

Debugging with dap-mode

dap-mode is an excellent package for bringing rich debugging capabilities to Emacs via the Debug Adapter Protocol. You should check out the configuration docs to learn how to configure the debugger for your language. Also make sure to check out the documentation for the debug adapter to see what configuration parameters are available to use for your debug templates!

(use-package dap-mode
  :straight t
  :defer t
  :bind
  ((:map dap-mode-map
         ("C-x D D" . dap-debug)
         ("C-x D d" . dap-debug-last)))
  ;; (:map lsp-mode-map
  ;;       ("C-c l d" . (dap-hydra t :wk "debugger")))
  ;;
  ;; Uncomment the config below if you want all UI panes to be hidden by default!
  ;; :custom
  ;; (lsp-enable-dap-auto-configure nil)
  ;; :config
  ;; (dap-ui-mode 1)
  :commands dap-debug
  :config
  (setq dap-auto-configure-features '(sessions locals controls tooltip)
        dap-ui-mode 1))

IDE Features with lsp-mode

lsp-mode

We use the excellent lsp-mode to enable IDE-like functionality for many different programming languages via “language servers” that speak the Language Server Protocol. Before trying to set up lsp-mode for a particular language, check out the documentation for your language so that you can learn which language servers are available and how to install them.

The lsp-keymap-prefix setting enables you to define a prefix for where lsp-mode’s default keybindings will be added. I highly recommend using the prefix to find out what you can do with lsp-mode in a buffer.

The which-key integration adds helpful descriptions of the various keys so you should be able to learn a lot just by pressing C-c l in a lsp-mode buffer and trying different things that you find there.

(use-package lsp-mode
  :straight t
  :commands (lsp lsp-deferred)
  :hook
  ((python-mode emacs-lisp-mode c-mode c++-mode json-mode tex-mode) . lsp)
  :config
  (setq lsp-keymap-prefix                   "C-c l"   ;; Or 'C-l', 's-l'
        lsp-headerline-breadcrumb-segments  '(path-up-to-project file symbols))
  (lsp-enable-which-key-integration t)
  (lsp-headerline-breadcrumb-mode))

lsp-ui

lsp-ui is a set of UI enhancements built on top of lsp-mode which make Emacs feel even more like an IDE. Check out the screenshots on the lsp-ui homepage (linked at the beginning of this paragraph) to see examples of what it can do.

(use-package lsp-ui
  :straight t
  :commands lsp-ui-mode
  :config
  (setq lsp-ui-doc-position                 'bottom
        lsp-ui-doc-show-with-mouse          nil
        lsp-ui-peek-always-show             t
        lsp-ui-sideline-show-code-actions   t
        lsp-ui-sideline-show-hover          t)
  :bind (("C-c A" . lsp-execute-code-action)
         ("C-c d" . lsp-ui-doc-show)
         ("C-c I" . lsp-ui-imenu)))

lsp-treemacs

lsp-treemacs provides nice tree views for different aspects of your code like symbols in a file, references of a symbol, or diagnostic messages (errors and warnings) that are found in your code.

Try these commands with M-x:

  • lsp-treemacs-symbols - Show a tree view of the symbols in the current file
  • lsp-treemacs-references - Show a tree view for the references of the symbol under the cursor
  • lsp-treemacs-error-list - Show a tree view for the diagnostic messages in the project

This package is built on the treemacs package which might be of some interest to you if you like to have a file browser at the left side of your screen in your editor.

(use-package lsp-treemacs
  :after lsp)

(use-package treemacs-nerd-icons
  :config
  (treemacs-load-theme "nerd-icons"))

lsp-ivy

lsp-ivy integrates Ivy with lsp-mode to make it easy to search for things by name in your code. When you run these commands, a prompt will appear in the minibuffer allowing you to type part of the name of a symbol in your code. Results will be populated in the minibuffer so that you can find what you’re looking for and jump to that location in the code upon selecting the result.

Try these commands with M-x:

  • lsp-ivy-workspace-symbol - Search for a symbol name in the current project workspace
  • lsp-ivy-global-workspace-symbol - Search for a symbol name in all active project workspaces
(use-package lsp-ivy
  :after lsp)

Improved parenthesis

(use-package smartparens
  :straight t
  :hook prog-mode)

Lisp

Lispy

(use-package lispy
  :straight t
  :hook (emacs-lisp-mode lisp-mode scheme-mode))

Emacs Lisp

(use-package helpful
  :commands (helpful-callable helpful-variable helpful-command helpful-key)
  :bind
  ([remap describe-function] . helpful-function)
  ([remap describe-symbol] . helpful-symbol)
  ([remap describe-variable] . helpful-variable)
  ([remap describe-command] . helpful-command)
  ([remap describe-key] . helpful-key)
  :config
  (setq counsel-describe-function-function #'helpful-callable
        counsel-describe-variable-function #'helpful-variable))

SLY

For Common Lisp development, I use sly to interact with the lisp instance.

(defvar lisp-images-base-dir (concat
                              (get 'abbreviated-home-dir 'home)
                              "/.local")
  "This is the base directory in which to find various cl implementations.")

(defvar sbcl-core-for-sly "/lib/sbcl/sbcl.core-for-sly"
  "This is the relative path to the sly-specific core for sbcl.")

(defvar sbcl-latest-sly-core-file
  (concat lisp-images-base-dir "/sbcl/latest" sbcl-core-for-sly)
  "This is where to find ")

(use-package sly
  :straight t
  :mode ("\\.lisp\\'" . lisp-mode)
  :hook (lisp-mode . (lambda ()
                       (unless (sly-connected-p)
                         (save-excursion (sly)))))
  :init
  (setq sly-contribs              '(sly-fancy sly-retro sly-trace-dialog)
        sly-lisp-implementations  `((sbcl-latest
                                     ("sbcl" "--core"
                                      ,sbcl-latest-sly-core-file))
                                    (cmucl-latest ("lisp")))
        sly-net-coding-system     'utf-8-unix)
  :config
  ;; setup sly-documentation-lookup
  (eval-after-load 'sly
    `(define-key sly-prefix-map (kbd "M-h") 'sly-documentation-lookup))
  ;; setup sly-mrepl-clear-recent-output
  (eval-after-load 'sly-mrepl
    `(define-key sly-mrepl-mode-map (kbd "C-c C-k")
                 `sly-mrepl-clear-recent-output))
  :custom-face
  (sly-mrepl-output-face ((t (:forground "sienna")))))

Python

Language Server Protocol Support

We use lsp-mode and dap-mode to provide a more complete development environment for Python in Emacs.

There are a number of other language servers for Python so if you find that pyls doesn’t work for you, consult the lsp-mode language configuration documentation to try the others!

(use-package pylint)

(use-package python-docstring
  :config
  (python-docstring-install))

(use-package python-mode
  :straight t
  :bind
  (:map python-mode-map
        ("C-c C-p" . flycheck-previous-error)
        ("C-c C-n" . flycheck-next-error))
  :hook (python-mode . lsp-deferred)
  :init (require 'dap-python)
  :config
  (setq dap-python-debugger                         'debugpy
        python-flymake-command                      '("flake8" "-")
        python-indent-trigger-commands              '(yas-expand)
        python-shell-interpreter                    "jupyter"
        python-shell-interpreter-args               "console --simple-prompt"
        python-shell-prompt-detect-failure-warning  nil
        python-shell-prompt-output-regexp           "Out\\[0-9]+\\]: "
        python-shell-prompt-regexp                  "In \\[0-9]+\\]: "))

lsp-pyright

lsp-pyright intergrates lsp-mode with Microsoft’s pyright language server protocol support for python.

(use-package lsp-pyright
  :straight t
  :hook (python-mode . (lambda ()
                         (require 'lsp-pyright)
                         (lsp))))

Virtual Env Support

pyvenv

You can use the pyvenv package to use virtualenv environments in Emacs. The pyvenv-activate command should configure Emacs to cause lsp-mode and dap-mode to use the virtual environment when they are loaded, just select the path to your virtual environment before loading your project.

(use-package pyvenv
  :straight t
  :after python-mode
  :config
  (pyvenv-mode 1))
anaconda-mode

Another alternative is to use anaconda-mode.el, which depends on `setuptools` being installed in the default active conda environment.

(use-package anaconda-mode
  :straight t
  :after python-mode)

Formatting Support

black is the python formatter I prefer

(use-package blacken
  :straight t
  :after python-mode
  :hook python-mode)

Programming Practice

Leetcode

(use-package leetcode
  :straight t
  :commands leetcode-show
  :config
  (setq leetcode-prefer-language  "python3"
        leetcode-save-solutions t
        leetcode-directory "~/Dropbox/leetcode"))

Exercism

Exercism allows you to solve community provided problems in a variety of languages.

(use-package exercism
  :straight t)

Terminals

term-mode

term-mode is a built-in terminal emulator in Emacs. Because it is written in Emacs Lisp, you can start using it immediately with very little configuration. If you are on Linux or macOS, term-mode is a great choice to get started because it supports fairly complex terminal applications (htop, vim, etc) and works pretty reliably. However, because it is written in Emacs Lisp, it can be slower than other options like vterm. The speed will only be an issue if you regularly run console apps with a lot of output.

One important thing to understand is line-mode versus char-mode. line-mode enables you to use normal Emacs keybindings while moving around in the terminal buffer while char-mode sends most of your keypresses to the underlying terminal. While using term-mode, you will want to be in char-mode for any terminal applications that have their own keybindings. If you’re just in your usual shell, line-mode is sufficient and feels more integrated with Emacs.

Run a terminal with M-x term!

Useful key bindings: “`

  • C-c C-p / C-c C-n - go back and forward in the buffer’s prompts (also [[ and ]] with evil-mode)
  • C-c C-k - Enter char-mode
  • C-c C-j - Return to line-mode

“`

(use-package term
  :commands term
  :config
  (setq explicit-shell-file-name "zsh") ;; Change this to zsh, etc
  ;;(setq explicit-zsh-args '())         ;; Use 'explicit-<shell>-args for shell-specific args
  ;; Match the default Bash shell prompt.  Update this if you have a custom prompt
  (setq term-prompt-regexp "^[^#$%>\n]*[#$%>] *"))

Better term-mode colors

The eterm-256color package enhances the output of term-mode to enable handling of a wider range of color codes so that many popular terminal applications look as you would expect them to. Keep in mind that this package requires ncurses to be installed on your machine so that it has access to the tic program. Most Linux distributions come with this program installed already so you may not have to do anything extra to use it.

(use-package eterm-256color
  :hook term-mode)

vterm

vterm is an improved terminal emulator package which uses a compiled native module to interact with the underlying terminal applications. This enables it to be much faster than term-mode and to also provide a more complete terminal emulation experience.

Make sure that you have the necessary dependencies installed before trying to use vterm because there is a module that will need to be compiled before you can use it successfully.

(use-package vterm
  :straight t
  :bind
  (("C-x t" . vterm)
   (:map vterm-mode-map
         ("M-p" . vterm-send-up)
         ("M-n" . vterm-send-down)))
  :commands vterm
  :config
  ;; term-prompt-regexp should be modified to match your custom shell prompt
  ;; vterm-shell should be modified to control which shell to run
  (setq term-prompt-regexp    "^[^#$%>\n]*[#$%>] *"
        vterm-shell           "zsh"
        vterm-max-scrollback  10000))

multi-vterm

multi-vterm mode allows running multiple vterm buffers simultaneously.

(use-package multi-vterm
  :straight t
  :config
  (setq multi-vterm-dedicated-window-height-percent 10)
  :after vterm)

shell-mode

shell-mode is a middle ground between term-mode and Eshell. It is not a terminal emulator so more complex terminal programs will not run inside of it. It does have much better integration with Emacs because all command input in this mode is handled by Emacs and then sent to the underlying shell once you press Enter.

Useful key bindings: “`

  • C-c C-p / C-c C-n - go back and forward in the buffer’s prompts
  • M-p / M-n - go back and forward in the input history
  • C-c C-u - delete the current input string backwards up to the cursor
  • counsel-shell-history - A searchable history of commands typed into the shell

“`

One advantage of shell-mode on Windows is that it’s the only way to run cmd.exe, PowerShell, Git Bash, etc from within Emacs. Here’s an example of how you would set up shell-mode to run PowerShell on Windows:

(when (eq system-type 'windows-nt)
  (setq explicit-shell-file-name "powershell.exe")
  (setq explicit-powershell.exe-args '()))

Eshell

Eshell is Emacs’ own shell implementation written in Emacs Lisp. It provides you with a cross-platform implementation (even on Windows!) of the common GNU utilities you would find on Linux and macOS (ls, rm, mv, grep, etc). It also allows you to call Emacs Lisp functions directly from the shell and you can even set up aliases (like aliasing vim to find-file). Eshell is also an Emacs Lisp REPL which allows you to evaluate full expressions at the shell.

The downsides to Eshell are that it can be harder to configure than other packages due to the particularity of where you need to set some options for them to go into effect, the lack of shell completions (by default) for some useful things like Git commands, and that REPL programs sometimes don’t work as well. However, many of these limitations can be dealt with by good configuration and installing external packages, so don’t let that discourage you from trying it!

Useful key bindings: “`

  • C-c C-p / C-c C-n - go back and forward in the buffer’s prompts
  • M-p / M-n - go back and forward in the input history
  • C-c C-u - delete the current input string backwards up to the cursor
  • counsel-esh-history - A searchable history of commands typed into Eshell

“`

We will be covering Eshell more in future videos highlighting other things you can do with it.

For more thoughts on Eshell, check out these articles by Pierre Neidhardt:

(defun my/configure-eshell ()
  ;; Save command history when commands are entered
  (add-hook 'eshell-pre-command-hook 'eshell-save-some-history)

  ;; Truncate buffer for performance
  (add-to-list 'eshell-output-filter-functions 'eshell-truncate-buffer)

  (setq eshell-history-size               10000
        eshell-buffer-maximum-lines       10000
        eshell-hist-ignoredups            t
        eshell-scroll-to-bottom-on-input  t))

(use-package eshell-git-prompt
  :after eshell)

(use-package eshell
  :hook (eshell-first-time-mode . my/configure-eshell)
  :config
  (with-eval-after-load 'esh-opt
    (setq eshell-destroy-buffer-when-process-dies t
          eshell-visual-commands                  '("htop" "zsh" "vim")))
  (eshell-git-prompt-use-theme 'powerline))

Compilation

(use-package compile
  :straight t
  :defer t
  :hook
  (((c++-mode c-mode java-mode javascript-mode go-mode nroff-mode)
    .
    generic-compiler))
  :bind
  (("C-x M-m" . compile)
   ("C-x C-m" . recompile))
  :init
  (defun has-makefile-p ()
    (or (file-exists-p "makefile")
        (file-exists-p "Makefile")))

  (defun generic-compiler ()
    (unless (has-makefile-p)
      (setq-local compile-command
                  (concat "compiler "
                          (when buffer-file-name
                            (shell-quote-argument buffer-file-name))))))
  :config
  (setq compilation-scroll-output t)
  (require 'ansi-color)
  (defun colorize-compilation-buffer ()
    (let ((inhibit-read-only t))
      (ansi-color-apply-on-region (point-min) (point-max))))
  (add-hook 'compilation-filter-hook 'colorize-compilation-buffer))

Docker

(use-package docker
  :straight t
  :bind
  (("C-c d" . docker)))

Productivity

Improve PDF handling

(use-package pdf-tools
  :straight t
  :defer t
  :commands (pdf-view-mode pdf-tools-install)
  :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode)
  :magic ("%PDF" . pdf-view-mode)
  :config
  (pdf-tools-install)
  (define-pdf-cache-function pagelables)
  (setq-default pdf-view-display-size 'fit-page)
  (add-to-list 'org-file-apps
               '("\\.pdf\\'" . (lambda (file link)
                                 (org-pdftools-open link)))))

Syntax checking with Flycheck

(use-package flycheck
  :defer t
  :hook ((lsp-mode emacs-lisp-mode python-mode) . flycheck-mode))

Add python checkers

(use-package flycheck-prospector
  :straight t
  :defer t)

Snippets

(use-package yasnippet
  :straight t
  :hook (prog-mode . yas-minor-mode)
  :config
  (yas-reload-all)
  (yas-global-mode 1))

(use-package yasnippet-snippets
  :straight t
  :after yasnippet)

ChatGPT

;; replace with c3po.el

Applications

Some App

This is an example of configuring another non-Emacs application using org-mode. Not only do we write out the configuration at .config/some-app/config, we also compute the value that gets stored in this configuration from the Emacs Lisp block above it.

(+ 55 100)

NOTE: Set the :tangle parameter below to .config/some-app/config for this to work!

value=<<the-value()>>

Runtime Performance

Dial the GC threshold back down so that garbage collection happens more frequently but in less time.

;; ensure we don't compact font caches during gc
(setq inhibit-compacting-font-caches t)
;; Make gc pauses faster by decreasing the threshold.
(setq gc-cons-threshold (* 2 1000 1000))