dotfiles/.emacs.d/config.org

27 KiB

General Configuration

General configuration is configuration of vanilla Emacs. This includes making Emacs more minimal.

Disable Error Bell

Disable error bell and visual bell. Visual bell would be nice on Linux, but on mac it is just annoying so I removed it temp.

  (setq ring-bell-function 'ignore)
  (setq visual-bell nil)

Disable Dialog Box

Disables the dialog prompting box and instead prompts the user in the minibuffer.

(setq use-dialog-box nil)

Relocate Custom File

Change location of custom file. This will stop custom from appending to the config file.

  (setq custom-file "~/.emacs.d/custom.el")
  (if (not (file-exists-p custom-file))
      (make-empty-file custom-file))
  (load custom-file)

Move Backup File

This moves the backup files so that Emacs doesn't clutter up directories with backup files.

(setq backup-directory-alist '(("." . "~/.emacs.d/backups/")))

Time Startup

This function times the startup to tell you how long it took for the Emacs config to load.

(defun jm/display-startup-time ()
  (message "Emacs loaded in %.2f seconds with %d garbage collections."
           (float-time (time-subtract after-init-time before-init-time))
           gcs-done))

(add-hook 'emacs-startup-hook 'jm/display-startup-time)

White Space

Various configuration relating to white-space.

  (setq-default electric-ident-inhibit t) ; Stop indentation of the previous line.

Tabs/Spaces

Disable tabs and replace them with a custom number of spaces.

  (setq-default indent-tabs-mode nil) ; Use spaces instead of tabs.
  (setq-default tab-width 4)
  (setq-default evil-shift-width tab-width)

  ; C Indentation
  (setq-default c-basic-offset 4)

  ; Yara Indentation
  (setq-default yara-indent-offset 4)
  (setq-default yara-indent-section 4)

Visualizing White Space

Add a nice visualization for tabs and spaces. This can be helpful to identify which is which quickly to avoid submitting poorly spaced code.

  (global-whitespace-mode)
  (setq whitespace-global-modes '(not org-mode dired-mode))
  (setq whitespace-style '(face tabs spaces tab-mark space-mark trailing))
  (custom-set-faces
    '(whitespace-tab ((t (:foreground "#384551"))))
    '(whitespace-space ((t (:foreground "#384551")))))


  (setq whitespace-display-mappings
    '((tab-mark 9 [187 9] [92 9])
      (space-mark 32 [183] [46])))

SSH Remoting with Tramp

Below are a few configuration changes to avoid TRAMP freezes when attempting to SSH into another system with more complex config files.

(setq tramp-remote-shell "/bin/sh")
(setq tramp-remote-shell-login "-l")
(setq tramp-remote-shell-args "-c")

General Packages

Emacs and packages. Pretty much a requirement.

Setup package

Initialize package and setup package archives. This lets you install packages from other archives such as melpa.

  (require 'package)

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

  (package-initialize)
  (unless package-archive-contents
    (package-refresh-contents))

Use-package

Install use package for easier installation of other packages.

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

  (require 'use-package)
  (setq use-package-always-ensure t)

Fixes path issue that occurs on mac.

  (use-package exec-path-from-shell
    :config
    (when (memq window-system '(mac ns x))
      (exec-path-from-shell-initialize)))

Installed Packages

This is a list of installed packages not included in any other category.

Command Log Mode

Command Log Mode creates a window that logs all commands and corresponding keybindings.

    (use-package command-log-mode
      :defer
      :config (global-command-log-mode))

Magit

Magit adds several features to Emacs that make using git easier.

  (use-package magit)

Helpful

Helpful is a package that improves the builtin Emacs help menus.

  (use-package helpful
    :config ; Set keybindings to call helpful instead of the default emacs help.
    (global-set-key (kbd "C-h f") #'helpful-callable)
    (global-set-key (kbd "C-h v") #'helpful-variable)
    (global-set-key (kbd "C-h k") #'helpful-key)
    (global-set-key (kbd "C-h x") #'helpful-command)

    ;; Lookup the current symbol at point. C-c C-d is a common keybinding for this in lisp modes.
    (global-set-key (kbd "C-c C-d") #'helpful-at-point)

    ;; Look up *F*unctions (excludes macros).
    (global-set-key (kbd "C-h F") #'helpful-function)

    ;; Syncing with ivy and counsel
    (setq counsel-describe-function-function #'helpful-callable)
    (setq counsel-describe-variable-function #'helpful-variable))

Cosmetic

Packages that change the look of Emacs in some way.

Cosmetic Configuration

Disable the useless features that make Emacs bloated.

  ; Disable startup message
  (setq inhibit-startup-message t)

  (scroll-bar-mode -1)   ; Disable scrollbar
  (tool-bar-mode -1)     ; Disable toolbar
  (tooltip-mode -1)      ; Disable tooltips
  (menu-bar-mode -1)     ; Disable menu-bar
  (set-fringe-mode 10)   ; Add gaps on left and right
  (setq scroll-margin 4) ; Scroll as cursor reaches bottom/top of page.

Change font

Pretty self explanatory.

  (defun jm/set-font-faces ()
      (set-face-attribute 'default nil :font "Fira Code Retina" :height 140))

Adding a fix for fonts when running Emacs as a daemon.

  (if (daemonp)
      (add-hook 'after-make-frame-functions
                (lambda (frame)
                  (with-selected-frame frame
                    (jm/set-font-faces))))
    (jm/set-font-faces))

Doom Modeline

Use doom modeline to make the modeline look nicer.

    (use-package all-the-icons) ; requirement
    (use-package doom-modeline
      :init (doom-modeline-mode 1)
      :config
      (setq doom-modeline-height 45)
      (display-time))

In order to avoid missing icons, you should also install the package all-the-icons-install-fonts through the Emacs command line.

Doom Themes

Install doom themes for better themes.

  (use-package doom-themes
    :init (load-theme 'doom-city-lights t))

Rainbow Delimiters

Colors parenthesis for better lisp syntax highlighting.

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

Line Numbers

This adds relative line numbers while excluding certain modes.

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

  ; Enable relative line numbers
  (setq-default display-line-numbers-type 'visual)
  (global-display-line-numbers-mode t)

Managing Buffers

Perspective is a package to help with managing buffers. It allows for multiple workspaces or perspectives which each contain their own sub list of buffers.

(use-package perspective
  :bind (("C-x b" . persp-ivy-switch-buffer)
         ("C-x k" . persp-kill-buffer*))
  :custom (persp-mode-prefix-key (kbd "C-x w"))
  :init
  (persp-mode))

Auto-completion

Packages associated with Emacs auto-completion. This does not include auto-completion from language servers just the auto-completion of commands etc.

Counsel

Adds various completion functions used by Ivy.

  (use-package counsel
    :bind (("M-x" . counsel-M-x)
           ("C-x C-f" . counsel-find-file)
           :map minibuffer-local-map
           ("C-r" . counsel-minibuffer-history)))

Ivy

Ivy is a basic auto-completion package that completes Emacs functions.

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

Ivy-rich

Install Ivy-rich for function info in Ivy auto complete.

  (use-package ivy-rich
    :init (ivy-rich-mode 1))

Keybindings

Contains the configuration for any keybindings or packages relating to keybindings.

General Configurations

Remap quit command to make it easier to rescue a buffer. With this function, escape will be used instead of C-g.

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

Packages

Which-key

Lists all possible keybindings off of prefix.

  (use-package which-key
    :init (which-key-mode)
    :diminish which-key-mode)

General

General allows you to setup a prefix key easily. This makes it really easy to setup a bunch of keybindings with ease.

  (use-package general
    :config
    (general-create-definer jm/leader-keys
      :keymaps '(normal insert visual emacs)
      :prefix "SPC"
      :global-prefix "C-SPC")

    (jm/leader-keys
     ; Example of a keybinding should be changed later
     "t" '(counsel-load-theme :which-key "Choose theme")
     "v" '(multi-vterm :which-key "Open vterm terminal")
     "r" '(rename-buffer :which-key "Rename buffer")

     ; Org mode related keybindings
     "oa" '(org-agenda :which-key "Org agenda")

     ; Emacs related keybindings
     "er" '((lambda () (interactive) (load-file "~/.emacs")) :which-key "Reload emacs config")
     "es" '(eshell :which-key "Open eshell terminal")))

Evil

Evil is a package that adds vim keybindings to Emacs.

  ;; Setup vim keybindings with evil
  (use-package evil
    :init
    (setq evil-want-integration t)
    (setq evil-want-keybinding nil) ; replaced with evil collection
    (setq evil-want-C-u-scroll t)
    (setq evil-want-C-i-jump nil)
    :config
    (evil-mode 1)
    (define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
    (define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join)

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

    ; Modify which modes use vim keybindings.
    (evil-set-initial-state 'messages-buffer-mode 'normal)
    (evil-set-initial-state 'dashboard-mode 'normal))
Evil Collection

Evil collection is a package that replaces the bad evil-want-keybinding keybindings.

  (use-package evil-collection
    :after evil
    :config (evil-collection-init))
Undo-Tree

Undo tree's use is self explanatory. While the built-in Emacs undo system is fine for me, undo-tree is required as it fixes an issue with evil where you can't redo.

  ; Fix vim redo with undo tree
  (use-package undo-tree
    :after evil
    :config
    (evil-set-undo-system 'undo-tree)
    (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))
    (global-undo-tree-mode 1))

Hydra

Hydra is a package that implements a way to execute the same commands in quick succession.

  (use-package hydra :defer)

Setup easy changing of font size. This implements a zooming system to make text smaller or bigger quickly.

  (defhydra hydra-zoom (:timeout 4)
    ("j" text-scale-increase "in")
    ("k" text-scale-decrease "out")
    ("f" nil "finished" :exit t))

This keybinding needs to be added to general to give it a prefix.

  (jm/leader-keys
  "z" '(hydra-zoom/body :which-key "scale-text"))

Dired

Dired is a built-in package in Emacs that allows for basic file navigation. While it serves its purpose, vanilla dired is far from a good file navigator. With some basic customization however, this can be changed.

  (use-package dired
    :ensure nil ; Melpa won't be able to find this package.
    :commands (dired dired-jump)
    :bind (("C-x C-j" . dired-jump))
    :custom ((dired-listing-switches "-ahgo"))
    :config
    (evil-collection-define-key 'normal 'dired-mode-map
      "h" 'dired-single-up-directory
      "l" 'dired-single-buffer))

Dired Single

Vanilla dired opens a new buffer for every new directory it visits. When managing files, this will quickly fill up resulting in a ridiculous number of buffers. Though, single dired fixes this problem by instead modifying the current buffer when navigating through files.

  (use-package dired-single :after dired)

All the Icons Dired

all-the-icons-dired is a dired plugin that adds icons to each of the files.

  (use-package all-the-icons-dired
    :after dired
    :hook (dired-mode . all-the-icons-dired-mode))

Hide Dotfiles

This hides all dotfiles in dired with the keybinding H.

(use-package dired-hide-dotfiles
  :hook (dired-mode . dired-hide-dotfiles-mode)
  :config
  (evil-collection-define-key 'normal 'dired-mode-map
    "H" 'dired-hide-dotfiles-mode))

Org Mode

Org is a package that allows you to create files like this one that look nice while also being able to run code. In this file, the code being run is stored in code blocks and all other text is disregarded.

Org Setup

This installs the org package and creates a setup function to enable/disable certain functionalities.

;; Setup org mode
(defun jm/org-mode-setup ()
  (org-indent-mode)
  (variable-pitch-mode 1)
  (visual-line-mode 1)
  (add-to-list 'org-link-frame-setup '(file . find-file))) ; Open link in current window not other window.

(use-package org
  :hook (org-mode . jm/org-mode-setup)
  :config
  (setq org-ellipsis " ▾"
        org-hide-emphasis-markers t
        org-src-preserve-indentation t
        org-export-with-toc nil
        org-export-with-section-numbers nil
        org-export-with-sub-superscripts nil))

Org Agenda

Org mode by default contains an agenda system which is like a basic calendar that allows you to schedule todo items from org documents. All of the todo items from each org document are stored in a central area to allow for a formation of an agenda.

(setq org-agenda-start-with-log-mode t)
(setq org-log-done 'time)
(setq org-log-into-drawer t)

Refresh Org Agenda Files

Creates a function to refresh the org-agenda-files variable to be set to include all org roam notes files.

(defun jm/org-roam-refresh-agenda-list ()
  (interactive)
  (setq org-agenda-files (mapcar #'org-roam-node-file (org-roam-node-list))))

Custom States

Adds custom states to tasks such as NEXT, CANCELLED, etc.

(setq org-todo-keywords '((sequence "TODO(t)" "|" "DONE(d)" "CANCELLED(c)")))

Refresh Checkboxes

Adds a hook to repeated tasks in org agenda that, when repeated, checkboxes will be reset to an unchecked state.

(add-hook 'org-todo-repeat-hook #'org-reset-checkbox-state-subtree)

Cosmetics

Org-Bullets

Org-bullets is a package that adds bullets to each heading instead of asterisks. It just makes org files nicer to look at.

  ; Org-bullets for better headings
  (use-package org-bullets
    :after org
    :hook (org-mode . org-bullets-mode)
    :custom
    (org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●")))

Add List Dots

By default lists are started with a hyphen, though this doesn't really match the aesthetic of the rest of the org file. Due to that, I added this line which replaces the hyphen with a dot.

  • Bullet point 1
  • Bullet point 2
  • Bullet point 3
  ; Replace - lists with a dot
  (font-lock-add-keywords 'org-mode
                            '(("^ *\\([-]\\) "
                               (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))

Font Changes

Org-faces changes the font size of the headings to make them bigger in the org file. Though this code also changes the font to a variable-pitch font. To make it so that only fixed-pitch fonts are used in things like code blocks, set-face-attribute is used below.

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

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

Visual Fill Column

Visual fill column is a package that allows you to center text and add borders to the sides of an org file. By default org files are displayed completely to the left side of the page like normal text files.

  ; Set left-right margins with visual-fill-column
  (defun jm/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
    :after org
    :hook (org-mode . jm/org-mode-visual-fill))

Org Roam

Org-roam is a plain-text knowledge management system. It brings some of Roam's more powerful features into the Org-mode ecosystem such as org-file linking, etc.

(setq jm/org-roam-directory "~/Notes")
(unless (file-directory-p jm/org-roam-directory)
  (make-directory jm/org-roam-directory))

(use-package org-roam
  :ensure t
  :defer
  :custom
  (org-roam-directory jm/org-roam-directory)
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n g" . org-roam-graph)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n c" . org-roam-capture)
         ;; Dailies
         ("C-c n j" . org-roam-dailies-capture-today))
  :config
  ;; If you're using a vertical completion framework, you might want a more informative completion interface
  (setq org-roam-capture-templates
        '(("d" "default" plain "%?"
           :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
           :unnarrowed t)
          ("p" "Project" plain
           (file "~/Notes/Templates/Project.org")
           :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
           :unnarrowed t)))
  (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
  (jm/org-roam-refresh-agenda-list)
  (org-roam-db-autosync-mode))

Fix Snippets

This is required as of org 9.2 as snippets such as <s don't work. Without this fix, you are required to manually type out structures like code blocks.

  (require 'org-tempo)

Ispell

Configure Ispell to not spell check certain org mode specific keywords. By default, Ispell doesn't have a default dictionary selected. This can be changed by running ispell-change-dictionary. If no dictionaries are found, you can install them from the hunspell-en_us pacman package.

(add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:")) ;
(add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC"))
(add-to-list 'ispell-skip-region-alist '("~" . "~"))
(add-to-list 'ispell-skip-region-alist '("\\[\\[" . "\\]"))

Language Server Protocol

Language servers provide auto completion and syntax highlighting capabilities making them essential for development.

Lsp Mode

Lsp Mode is a package that adds language server functionalities to Emacs.

Breadcrumb Header Line

This adds a headerline that shows the scope of where the cursor is in the code. For example if the user is in the main function, the headerline will contain main.

  (defun jm/lsp-mode-setup ()
    (setq lsp-headerline-breadcrumb-segments '(path-up-to-project file symbols))
    (lsp-headerline-breadcrumb-mode))

Install Lsp Mode

Below I am installing the actual package and adding some basic configuration.

  (use-package lsp-mode
    :init (setq lsp-keymap-prefix "C-c l") ; Lsp mode prefix
    :hook (lsp-mode . jm/lsp-mode-setup)
    :commands (lsp lsp-deferred) ; Startup commands
    :config (lsp-enable-which-key-integration t))

Lsp Additional Packages

Lsp-UI
  (use-package lsp-ui
    :hook (lsp-mode . lsp-ui-mode)
    :config (setq lsp-ui-sideline-show-diagnostics t))
Lsp-Ivy
  (use-package lsp-ivy :commands lsp-ivy-workspace-symbol)

Company Mode

Company is a package that automatically finds completions instead of making the user run a command for completions.

  (use-package company
    :after lsp-mode
    :hook (lsp-mode . company-mode)
    :bind (:map company-active-map ; Map tab to select completion
                ("<tab>" . company-complete-selection))
          (:map lsp-mode-map
                ("<tab>" . company-indent-or-complete-common)))

Flycheck

  (use-package flycheck
    :init (global-flycheck-mode))

Language Servers

This will include any language server packages and configuration.

HTML

  (use-package web-mode
    :mode "\\.html\\'"
    :hook (web-mode . lsp-deferred))

JavaScript/Typescript

  (use-package typescript-mode
    :mode ("\\.ts\\'" "\\.js\\'")
    :hook (typescript-mode . lsp-deferred))

Install the typescript-language-server through npm.

  npm install -g typescript-language-server

Python

  (use-package python-mode
    :mode ("\\.py\\'")
    :hook (python-mode . lsp-deferred))

Install the python-lsp-server though pip.

  pip install python-lsp-server

C/C++

  (use-package ccls
    :hook ((c-mode cc-mode c++-mode objc-mode cuda-mode) .
          (lambda () (require 'ccls) (lsp)))
    :config
    (setq ccls-executable "/usr/bin/ccls"))

Install ccls with homebrew using the following command.

  sudo apt install ccls

Golang

  (use-package go-mode
     :hook (go-mode . lsp-deferred))

Rust

  (use-package rustic
     :hook (rust-mode . lsp-deferred))

C#

  (use-package csharp-mode
     :hook (csharp-mode . lsp-deferred))

JSON

  (use-package json-mode
    :hook (json-mode . lsp-deferred))

Yaml

  (use-package yaml-mode
    :hook (yaml-mode . lsp-deferred))

Yara

To install the corresponding LSP, you need to install yls. Using the command pip install -U yls-yara. Keep in mind that yls is dependent on an up to date version of yara-python.

  (use-package yara-mode
    :hook (yara-mode . lsp-deferred)
    :config
     (with-eval-after-load 'lsp-mode
      (add-to-list 'lsp-language-id-configuration
                   '(yara-mode . "yara"))

      (lsp-register-client
       (make-lsp-client :new-connection (lsp-stdio-connection "yls")
                        :activation-fn (lsp-activate-on "yara")
                        :server-id 'yls))))

R

  (use-package ess
    :hook (R-mode . lsp-deferred))

Terminal

Emacs also has the functionality to run a terminal environment. While many other terminals will try to have similar capabilities with keybindings, nothing matches just integrating your terminal in emacs.

VTerm

VTerm is a terminal emulator written in C. While emacs has a few built-in terminal all of them either lack speed or are missing many escape sequences.

  (use-package vterm
    :commands vterm
    :config
    (setq vterm-max-scrollback 10000)
    ; Fixes vterm issue with cursor not updating
    (advice-add #'vterm--redraw :after (lambda (&rest args) (evil-refresh-cursor evil-state))))

There are a few packages that you need to install in order to use VTerm.

  sudo apt install cmake libtool-bin

Multi-Vterm

Multi-Vterm is a package that allows for multiple vterm terminals to be used. By default vterm creates one *vterm* buffer. This buffer needs to be renamed in order to use multiple terminals.

  (use-package multi-vterm
    :after vterm)

Change Terminal Font

When using zsh with powerlevel10k, the MesloLGS NF font is required to make the prompt align properly. Due to this, this code is implemented to change the font only when VTerm is being used.

  (add-hook 'vterm-mode-hook
            (lambda ()
              (set (make-local-variable 'buffer-face-mode-face) '(:family "MesloLGS NF" :height 135))
              (buffer-face-mode t)))

Import Other Files

This is the section to include imports from other files.

;; Uncomment this to use EXWM. I currently don't use this.
;(let ((exwm-org-file "~/.emacs.d/exwm.org"))
;(when (file-exists-p exwm-org-file)
;  (org-babel-load-file exwm-org-file)))

;; Load org file containing custom Elisp functions.
(let ((functions-file "~/.emacs.d/functions.org"))
  (when (file-exists-p functions-file)
    (org-babel-load-file functions-file)))