Notes about my personal Emacs configuration.

Figure 1: Dashboard

I had been using Doom Emacs as a starter kit for my Emacs configuration, given that I am still not an Emacs/Elisp power user.

However, recently I was feeling that executing doom sync or doom upgrade frequently took way too much time, because it uses straight behind the scenes. So, long story short, I decided to write my own configuration from scratch, using the awesome package manager elpaca.

Installing packages with elpaca is way faster. It has integration with use-package, which is very convenient.

Installing Emacs

On macOS Sonoma

I have tried several options:

  • emacs-plus. This was my choice for a long time. Easy to install using homebrew. Worked like a charm.
  • emacs-mac. I wanted to try this because I had read that it was a very good choice. However, once I could compile it from source and get it to work on Apple Silicon, I found that it had problems rendering appropriately the fonts, as well as being unable to run Emacs as a daemon. Dissapointing, indeed, after all the work that it took me to try this option.

And finally, I found jime/emacs-builds, that has:

  • Native compilation
  • Native JSON parsing
  • SVG rendering
  • xwidget-webkit support
  • and other goodies

For the moment, I’ve decided to use the emacs-app-monthly build (with native Apple Silicon support), updated each month, and installed with homebrew:

brew install --cask emacs-app-monthly

On Windows

It’s a pain for me…, but I also need to work on Windows and wanted to have a working Emacs installation that worked on Windows. The simplest way to get one is to install a prebuilt binary, and forget about it. One way to get an Emacs build is using msys2, and install it with:

pacman -S ucrt64/mingw-w64-ucrt-x86_64-emacs

which (at the moment of writing), will give you version 29.4.

However, this won’t give me some of the good stuff I use on macOS/Linux. So I tried building it from source instead. I found kiennq/emacs-build, which seemed to be helpful. Building with some of the desired flags took considerable time, and I had partial success (no imagemagick support).

Finally, I decided for a different approach and try with an Ubuntu 22.04 WSL2 installation. This proved to be a much better experience. Even the build went faster. For this, I have used the abidanBrito/build-emacs.sh gist with slight modifications. The first one, and very important, was to use emacs-30 branch for the source code. Then, as I wanted xwidget support, I enabled the line to add the libwebkit2gtk-4.1-dev dependency. I added some compilation flags and what I finally used was:

./autogen.sh \
    && ./configure \
    --with-native-compilation \
    --with-pgtk \
    --with-x-toolkit=gtk3 \
    --with-tree-sitter \
    --with-wide-int \
    --with-json \
    --with-modules \
    --without-dbus \
    --with-gnutls \
    --with-mailutils \
    --without-pop \
    --with-cairo \
    --with-imagemagick \
    --with-xwidgets \
    --with-poll

It worked like a charm, and the resulting binary was faster than the one that I was using from msys2. Also, I didn’t had to deal with some of the nasty specifics of Windows (like configuring stuff to get symlinks to work on msys2 and elpaca).

My early-init.el

As I decided to go with elpaca, I simply used the recommended configuration by progfolio:

(setq package-enable-at-startup nil)

My init.el

I have decided to split my configuration into different files, trying to organize the contents according to the functionality provided by each of them. So, it looks like this (I will keep improving it, so it might change):

;; -*- lexical-binding: t; -*-

(add-to-list 'load-path (expand-file-name "my" user-emacs-directory))

(load "my-elpaca")
(load "my-mac")
(load "my-keys")
(load "my-treemacs")
(load "my-magit")
(load "my-tabs")

(load "my-completion")
(load "my-tabnine")
(load "my-lsp-bridge") ;; Includes some Java customizations.
(load "my-treesit")
(load "my-clojure")
(load "my-db")
(load "my-jupyter")
(load "my-writing")
(load "my-fonts")
(load "my-theme")

;; Local Variables:
;; no-byte-compile: t
;; no-native-compile: t
;; no-update-autoloads: t
;; End:

my/my-elpaca.el

Nothing special here. Exact same contents recommended by progfolio in the github repo for elpaca.

my/mac.el

This has some important stuff to integrate Emacs with macOS specific choices.

For example, I use fish as my shell. To combine that with exec-path-from-shell:

(use-package exec-path-from-shell
  :ensure (:wait t
           :depth 1)
  :demand t
  :init
  (setq exec-path-from-shell-shell-name "/opt/homebrew/bin/fish")
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))

;; This is necessary to avoid a bug
;; https://github.com/d12frosted/homebrew-emacs-plus/issues/554#issuecomment-1564287827
(setenv "LIBRARY_PATH"
  (mapconcat 'identity
   '("/opt/homebrew/opt/gcc/lib/gcc/14/"
     "/opt/homebrew/opt/libgccjit/lib/gcc/14/"
     "/opt/homebrew/opt/gcc/lib/gcc/14/gcc/aarch64-apple-darwin23/14/")
     ":"))

my/my-keys.el

I like modal editing, like vim/neovim/DOOM Emacs uses. So for the moment I am using this (among other things):

(use-package evil
  :ensure (:host github
           :repo "emacs-evil/evil"
           :depth 1)
  :init
  (setq evil-undo-system 'undo-fu)
  (setq evil-want-integration t) ;; This is optional since it's already set to t by default.
  (setq evil-want-keybinding nil)
  :config
  (evil-mode 1))

(use-package evil-collection
  :after evil
  :ensure (:host github
           :repo "emacs-evil/evil-collection"
           :depth 1)
  :config
  (evil-collection-init))

(use-package which-key
  :ensure (:host github
           :repo "justbur/emacs-which-key"
           :depth 1)
  :init
  (setq which-key-allow-evil-operators t
        which-key-show-operator-state-maps t)
  :config
  (which-key-mode))

my/my-completion.el

Some of the packages used in my current configuration:

my/my-tabnine.el

I’ve chose tabnine instead of copilot. It gives me the chance to get protection from risk and IP liability (e.g. Tabnine’s Protected model only trains on permissively licensed code).

Current config:

;; -*- lexical-binding: t; -*-
(use-package tabnine
  :ensure
         (:host github
          :repo "shuxiao9058/tabnine"
          :depth 1)
  :commands (tabnine-start-process)
  :diminish "⌬"
  :custom
  (tabnine-wait 1)
  (tabnine-minimum-prefix-length 0)
  :hook ((prog-mode . tabnine-mode)
    (kill-emacs . tabnine-kill-process))
  :config
  (add-to-list 'completion-at-point-functions #'tabnine-completion-at-point)
  (tabnine-start-process)
  (setq tabnine-auto-balance t)
  :bind
  (:map  tabnine-completion-map
           ("<tab>" . tabnine-accept-completion)
           ("TAB" . tabnine-accept-completion)
           ("M-f" . tabnine-accept-completion-by-word)
           ("M-<return>" . tabnine-accept-completion-by-line)
           ("C-g" . tabnine-clear-overlay)
           ("M-[" . tabnine-previous-completion)
           ("M-]" . tabnine-next-completion)))

(provide 'my-tabnine)

;; Local Variables:
;; no-byte-compile: t
;; no-native-compile: t
;; no-update-autoloads: t
;; End:

my/my-lsp-bridge.el

I had been using lsp-mode for Language Server Protocol (LSP) for Emacs. However, I recently wanted to try the (alledgedly) faster manateelazycat/lsp-bridge.

It depends on python packages. Although I have tried several “package” managers (like mise), when python is involved, it’s hard to beat conda’s anaconda. I chose miniforge (includes mamba, conda-forge is set as the default –and only– channel, supports Apple Silicon), and installed it with:

brew install miniforge

After that, it’s necessary to execute

conda init fish

so that conda works OK with fish.

For the moment, as I am not a Python dev myself, I installed lsp-bridge dependencies in the base environment. Something like:

conda activate base
conda install epc orjson sexpdata six setuptools paramiko rapidfuzz

conda has no trouble installing orjson (based on rust, for faster performance).

So, now python is found here:

❯ which python
/opt/homebrew/Caskroom/miniforge/base/bin/python

So, lsp-bridge configuration ended up like:

;; -*- lexical-binding: t; -*-

(use-package markdown-mode
  :ensure (:host github
           :repo "jrblevin/markdown-mode"
           :depth 1)
  :mode ("README\\.md\\'" . gfm-mode)
  :init (setq markdown-command "multimarkdown")
  :config (setq markdown-fontify-code-blocks-natively t)
  :bind (:map markdown-mode-map
              ("C-c C-e" . markdown-do)))

(use-package yasnippet
  :ensure
  (:host github
   :repo "joaotavora/yasnippet"
   :depth 1)
  :init
  (yas-global-mode 1))

(use-package lsp-bridge
  :ensure (:host github
           :repo "manateelazycat/lsp-bridge"
           :depth 1
           :files (:defaults "*.el" "*.py" "acm" "core" "langserver" "multiserver" "resources")
           :build (:not compile))
  :init
  (global-lsp-bridge-mode)
  (require 'lsp-bridge-jdtls)
  (setq lsp-bridge-python-command "/opt/homebrew/Caskroom/miniforge/base/bin/python"
        lsp-bridge-enable-auto-import t
        lsp-bridge-jdtls-jvm-args '("-javaagent:/Users/oscarvarto/.m2/repository/org/projectlombok/lombok/1.18.34/lombok-1.18.34.jar")
        acm-enable-tabnine t)
  :config
  (add-to-list 'lsp-bridge-default-mode-hooks 'tabnine-mode))

(defun apply-spotless-to-current-buffer ()
  "Apply Spotless formatter to the current buffer."
  (interactive)
  (when (buffer-file-name)
    (save-buffer)
    (let* ((file (buffer-file-name))
           (project-root (projectile-project-root)))
      (if project-root
          (let ((default-directory project-root))
            (shell-command (format "mvn spotless:apply -DspotlessFiles=%s" file))
            (revert-buffer t t t)
            (message "Spotless applied to %s in project %s" file project-root))
        (message "Could not find project root. Is Projectile installed and configured?")))))

(global-set-key (kbd "C-c s") 'apply-spotless-to-current-buffer)

(provide 'my-lsp-bridge)

;; Local Variables:
;; no-byte-compile: t
;; no-native-compile: t
;; no-update-autoloads: t
;; End:

This is also integrated with tabnine, and has some specifics configuration to execute mvn spotless:apply to current (Java) buffer (considering that projectile is already installed and configured; projectile here is an aid to find the root of the project). spotless-maven-plugin itself deserves it’s own post.

my/my-clojure.el

I’d love to get deeper into clojure. The following configuration sets up CIDER, and has some auxiliary elisp functions to work with the repl.

;; -*- lexical-binding: t; -*-

(require 'my-treesit)
(dolist (fn '(#'tree-sitter-mode #'tree-sitter-hl-mode))
  (add-hook 'clojure-mode-hook fn)) ;;#'tree-sitter-mode #'tree-sitter-hl-mode)

(dolist (pkg '(parseedn cider))
  (eval `(use-package ,pkg
           :ensure t)))

(with-eval-after-load 'cider
  (require 'projectile)
  (require 'parseedn)
  (require 'cider-overlays)
  (require 'cider-repl)

  (setq cider-overlays-use-font-lock t)
  (setq cider-repl-use-content-types t)

  (setq cider-ns-code-reload-tool 'clj-reload)

  (defun get-project-root ()
    "Get the root directory of the current project."
    (if (projectile-project-p)
      (projectile-project-root)
      default-directory))

  (defun extract-ns-default (deps-file)
    "Extract the ns-default from the :run-x alias in deps.edn."
    (if (file-exists-p deps-file)
      (with-temp-buffer
        (insert-file-contents deps-file)
        (goto-char (point-min))
        (let* ((deps-data (parseedn-read-str (buffer-string)))
               (aliases (gethash :aliases deps-data))
               (run-x (gethash :run-x aliases)))
          (gethash :ns-default run-x)))
      nil))

  (defun set-cider-repl-init-code ()
    (let ((ns (extract-ns-default (concat (get-project-root) "deps.edn"))))
      (if ns
          (format "(require '[%s :as mm]
                            '[clj-reload.core :as reload])
(reload/init
  {:dirs [\"src\" \"dev\" \"test\"]})
" ns)))) 

  ;; Hook into CIDER's jack-in hook to set the init code dynamically
  (add-to-list 'cider-repl-init-code (set-cider-repl-init-code) t)
  ;;(add-hook! 'cider-repl-mode-hook #'company-mode #'yas-minor-mode)
  (add-hook 'cider-repl-mode-hook
            (lambda ()
              ;; (company-mode)
              (yas-minor-mode-on)
              (yas-define-snippets 'cider-repl-mode
                                   '(("doc" "(clojure.repl/doc $0)" nil nil nil nil nil)
                                     ("r" "(reload/reload)$0" nil nil nil nil nil)
                                     )))))

(provide 'my-clojure)

;; Local Variables:
;; no-byte-compile: t
;; no-native-compile: t
;; no-update-autoloads: t
;; End:

Even when I don’t work frequently with clojure, CIDER is a dependency for clomacs, which in turn is a pre-requisite for ejc-sql (which turns Emacs into a simple SQL client). So, having this up and running is awesome for more than one reason.

my/my-db.el

I mainly use MySQL. So, I installed my-sql-connector-j manually using maven from the command line:

mvn org.apache.maven.plugins:maven-dependency-plugin:get -Dartifact=com.mysql:mysql-connector-j:9.0.0

This will take care of downloading all the transitive dependencies and then I can forget about it.

My current configuration for ejc-sql looks like:

;;; my-db.el -*- lexical-binding: t; -*-

;;--------------------------------------------------------------------
;; Emacs SQL client `ejc-sql'.
;;
(use-package ejc-sql
  :ensure t
  :config
  ;; Require completion frontend (autocomplete or company). One of them or both.
  ;;(require 'ejc-autocomplete)
  (require 'ejc-company)

  (require 'my-writing)
  (setq nrepl-sync-request-timeout 60)
  (setq clomacs-httpd-default-port 8090) ; Use a port other than 8080.
  ;; Allow use any CIDER nREPL not only library dedicated nREPL
  (setq clomacs-allow-other-repl t)

  ;; Show results of SQL snippets evaluation in `org-mode'
  ;; in dedicated buffer.
  (setq ejc-org-mode-show-results nil)
  ;;(setq ejc-use-flx t)                          ; Enable `flx' fuzzy matching.
  (setq ejc-completion-system 'standard)
  ;;(setq ejc-result-table-impl 'ejc-result-mode) ; Set major-mode for results.
  (setq ejc-result-table-impl 'orgtbl-mode)       ; Default major-mode for results.

  ;; Since `winner-mode' is enabled and M-<arrow> keys are used for
  ;; windows navigation, so disable this keys for `orgtbl-mode-map'.
  (define-key orgtbl-mode-map (kbd "<return>") nil)
  (define-key orgtbl-mode-map (kbd "M-<left>") nil)
  (define-key orgtbl-mode-map (kbd "M-<right>") nil)
  (define-key orgtbl-mode-map (kbd "M-<down>") nil)
  (define-key orgtbl-mode-map (kbd "M-<up>") nil)
  ;; Use C-M-<arrow> keys instead.
  (define-key orgtbl-mode-map (kbd "C-M-<left>") 'org-table-move-column-left)
  (define-key orgtbl-mode-map (kbd "C-M-<right>") 'org-table-move-column-right)
  (define-key orgtbl-mode-map (kbd "C-M-<up>") 'org-table-move-row-up)
  (define-key orgtbl-mode-map (kbd "C-M-<down>") 'org-table-move-row-down)
  ;; Add run SQL key familiar to users of PLSQL Developer.
  (define-key ejc-sql-mode-keymap (kbd "<F8>") 'ejc-eval-user-sql-at-point)

  (defun k/ejc-after-emacs-init-hook ()
    (push 'ejc-company-backend company-backends)
    ;; In case of `company-mode' is used by default this can be useful:
    (company-quickhelp-mode))

  (add-hook 'after-init-hook 'k/ejc-after-emacs-init-hook)

  (defun k/sql-mode-hook ()
    (ejc-sql-mode t))

  (add-hook 'sql-mode-hook 'k/sql-mode-hook)

  (defun k/ejc-result-mode-hook ()
    (display-line-numbers-mode))

  (add-hook 'ejc-result-mode-hook 'k/ejc-result-mode-hook)

  (defun k/ejc-sql-mode-hook ()
    ;; Enable one of the completion frontend by default but not both.
    ;;(auto-complete-mode t) ; Enable `auto-complete-mode'
    ;;(ejc-ac-setup)
    (company-mode t)       ; or `company-mode'.
    (ejc-eldoc-setup)      ; Setup ElDoc.
    (rainbow-delimiters-mode t) ; https://github.com/Fanael/rainbow-delimiters
    (idle-highlight-mode t)     ; https://github.com/nonsequitur/idle-highlight-mode
    (electric-pair-mode))

  (add-hook 'ejc-sql-minor-mode-hook 'k/ejc-sql-mode-hook)

  (defun k/ejc-sql-connected-hook ()
    (ejc-set-fetch-size 99)         ; Limit for the number of records to output.
    (ejc-set-max-rows 99)           ; Limit for the number of records in ResultSet.
    (ejc-set-show-too-many-rows-message t) ; Set output 'Too many rows' message.
    (ejc-set-column-width-limit 25) ; Limit for outputting the number of chars per column.
    (ejc-set-use-unicode t))        ; Use unicode symbols for grid borders.

  (add-hook 'ejc-sql-connected-hook 'k/ejc-sql-connected-hook)

  (global-set-key (kbd "C-c eb") 'ejc-get-temp-editor-buffer)

  ;; Load file with actual connections configurations
  ;; `ejc-create-connection' calls.
  ;;(require 'ejc-databases nil 'noerror)
  ;; mvn org.apache.maven.plugins:maven-dependency-plugin:get
  ;;     -Dartifact=com.mysql:mysql-connector-j:9.0.0
  (ejc-create-connection
   "SQLNovel"
   :classpath (concat "~/.m2/repository/com/mysql/mysql-connector-j/9.0.0/"
                       "mysql-connector-j-9.0.0.jar")
   :subprotocol "mysql"
   :subname "//localhost:3306/SQLNovel?autoReconnect=true&useSSL=false"
   :user "oscarvarto"
   :password "super-secret-password"))

(provide 'my-db)

;;; my-db.el ends here

So, after having created an ejc connection, I can use Emacs as a (FREE!!!) SQL client as well 😎! (powered by alien tech: Clojure).

To improve on the configuration above, it’s possible to plug-in something like auth-source-1password (1password is awesome!) to avoid putting your secrets/passwords in your Emacs configuration in plain text:

(use-package auth-source-1password
  :ensure (:host github
           :repo "dlobraico/auth-source-1password"
           :depth 1)
  :config
  (auth-source-1password-enable))

Then the :password keyword can be substituted by something like:

:password (auth-source-pick-first-password :host "my-host" :user "password")

my/my-writing.el

org-mode is important and useful to me. To create beautiful PDF documentation (using xelatex, python’s pygments + minted) I settled on the following:

(setq org-latex-packages-alist '(("top=1.5cm, bottom=3cm, left=1.5cm, right=1.5cm" "geometry" nil)
                                 ("" "minted"))
      org-latex-caption-above nil
      org-latex-listings 'minted
      org-latex-hyperref-template "\\hypersetup{\n pdfauthor={%a},\n pdftitle={%t},\n pdfkeywords={%k},\n pdfsubject={%d},\n pdfcreator={%c}, \n pdflang={%L},\n colorlinks=true,\n linkcolor=blue,\n urlcolor=blue,\n citecolor=blue,\n filecolor=blue,\n pdfborder={0 0 0}\n}"
      org-latex-pdf-process '("latexmk -xelatex -shell-escape -interaction=nonstopmode -output-directory=%o %f"))

(with-eval-after-load 'ox-latex
  (add-to-list 'org-latex-classes
               '("scrartcl" "\\documentclass{scrartcl}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))

And also this at the beggining of a document:

#+title: My useful and pretty documentation TITLE.

#+LATEX_CLASS: scrartcl
#+LATEX_CLASS_OPTIONS: [letter, 10pt]
#+LATEX_HEADER: \usepackage[top=1.5cm, bottom=3cm, left=1.5cm, right=1.5cm]{geometry}
#+LATEX_HEADER: \usepackage[T1]{fontenc}
#+LATEX_HEADER: \usepackage{lmodern}
#+LATEX_HEADER: \usepackage{fontspec}
#+LATEX_HEADER: \setmainfont{PragmataPro Liga}
#+LATEX_HEADER: \setmonofont{PragmataPro Mono Liga}
#+LATEX_HEADER: \newfontfamily\ppml{PragmataPro Mono Liga}[NFSSFamily=PPMLFamily]

#+LATEX_HEADER: \usepackage{minted}
#+LATEX_HEADER: \usepackage{xcolor}
#+LATEX_HEADER: \usepackage{framed}
#+LATEX_HEADER: \usepackage{caption}
#+LATEX_HEADER: \usepackage{upquote}

#+LATEX_HEADER: \definecolor{codebg}{RGB}{240,240,240}
#+LATEX_HEADER: \newenvironment{codeframe}{
#+LATEX_HEADER:   \begin{snugshade*}
#+LATEX_HEADER:   \begin{minipage}{\linewidth}
#+LATEX_HEADER: }{
#+LATEX_HEADER:   \end{minipage}
#+LATEX_HEADER:   \end{snugshade*}
#+LATEX_HEADER: }

#+LATEX_HEADER: \setminted{
#+LATEX_HEADER:   breaklines=true,
#+LATEX_HEADER:   fontsize=\footnotesize,
#+LATEX_HEADER:   frame=none,
#+LATEX_HEADER:   framesep=5pt,
#+LATEX_HEADER:   xleftmargin=5pt,
#+LATEX_HEADER:   xrightmargin=5pt,
#+LATEX_HEADER:   bgcolor=codebg,
#+LATEX_HEADER:   tabsize=2,
#+LATEX_HEADER:   breakafter=2,
#+LATEX_HEADER:   fontfamily=PPMLFamily,
#+LATEX_HEADER:   autogobble=true,
#+LATEX_HEADER: }

#+ATTR_LATEX: :float t :width \linewidth :placement [H] :environment codeframe
#+OPTIONS: toc:nil

NOTE: The above assumes you have installed PragmataPro fonts. You can change your choice of fonts to suit your needs.

org-modern

The following configuration makes your org documents look beatiful in Emacs itself:

;; Add frame borders and window dividers
(modify-all-frames-parameters
 '((right-divider-width . 40)
   (internal-border-width . 40)))
(dolist (face '(window-divider
                window-divider-first-pixel
                window-divider-last-pixel))
  (face-spec-reset-face face)
  (set-face-foreground face (face-attribute 'default :background)))
(set-face-background 'fringe (face-attribute 'default :background))

(use-package org-modern
  :ensure (:host github
           :repo "minad/org-modern"
           :depth 1)
  ;; :custom
  ;; (org-modern-keyword nil)
  ;; (org-modern-checkbox nil)
  ;; (org-modern-table nil)
  :config
  (setq
    ;; Edit settings
    org-auto-align-tags nil
    org-tags-column 0
    org-catch-invisible-edits 'show-and-error
    org-special-ctrl-a/e t
    org-insert-heading-respect-content t

    ;; https://github.com/minad/org-modern/discussions/227
    org-modern-star 'replace

    ;; Agenda styling
    org-agenda-tags-column 0
    org-agenda-block-separator ?─
    org-agenda-time-grid
    '((daily today require-timed)
      (800 1000 1200 1400 1600 1800 2000)
      " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")
    org-agenda-current-time-string
    "◀── now ─────────────────────────────────────────────────")

    ;; Ellipsis styling
    (setq org-ellipsis "…")
    (set-face-attribute 'org-ellipsis nil :inherit 'default :box nil)
    (global-org-modern-mode))

(use-package org-modern-indent
  :ensure (:host github
       :repo "jdtsmith/org-modern-indent"
       :depth 1)
  :config ; add late to hook
  (set-face-attribute 'fixed-pitch nil :family "PragmataPro Mono Liga" :height 1.0) ; or whatever font family
  (add-hook 'org-mode-hook #'org-modern-indent-mode 90))

I plan to write more on org as I learn more about it.

my/my-fonts.el

This is important to me. I found the following useful to get ligatures (with PragmataPro font) in GUI Emacs only:

;; -*- lexical-binding: t; -*-

(use-package all-the-icons-nerd-fonts
  :ensure
    (:host github
     :repo "mohkale/all-the-icons-nerd-fonts"
     :depth 1)
  :after all-the-icons
  :demand t
  :config
  (all-the-icons-nerd-fonts-prefer))

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

;; (use-package ligature
;;   :ensure (:host github
;;            :repo "mickeynp/ligature.el"
;;            :depth 1))
;; 
;; (add-to-list 'load-path (expand-file-name "~/pragmatapro/"))
;; (require 'pragmatapro-prettify-symbols-v0.830)
;; 
;; (defun conditionally-enable-pragmata-prettify ()
;;   "Enable `pragmata-prettify' only on GUI frames."
;;   (if (display-graphic-p)
;;       (progn
;;         (add-hook 'prog-mode-hook 'pragmata-prettify)
;;         (add-hook 'org-mode-hook 'pragmata-prettify)
;;         (ligature-mode 1)
;;         (global-prettify-symbols-mode 1))
;;     (remove-hook 'prog-mode-hook 'pragmata-prettify)
;;     (remove-hook 'org-mode-hook 'pragmata-prettify)
;;     (ligature-mode -1)
;;     (global-prettify-symbols-mode -1)))
;; 
;; (add-hook 'after-make-frame-functions #'conditionally-enable-pragmata-prettify)
;; (add-hook 'window-setup-hook #'conditionally-enable-pragmata-prettify)

(use-package pragmatapro-lig
  :ensure (:host github
           :repo "lumiknit/emacs-pragmatapro-ligatures"
           :depth 1)
  :config
  (defun conditionally-enable-pragmatapro-lig-mode ()
    "Enable `pragmatapro-lig' only on GUI frames."
    (if (display-graphic-p)
      (progn 
        (add-hook 'text-mode-hook 'pragmatapro-lig-mode)
        (add-hook 'prog-mode-hook 'pragmatapro-lig-mode))
      (remove-hook 'text-mode-hook 'pragmatapro-lig-mode)
      (remove-hook 'prog-mode-hook 'pragmatapro-lig-mode)))
  (add-hook 'after-make-frame-functions #'conditionally-enable-pragmatapro-lig-mode)
  (add-hook 'window-setup-hook #'conditionally-enable-pragmatapro-lig-mode))

;; Just use emacs-plus@30 instead of emacs-mac!
;; (setq-default line-spacing 5)
;; (setq default-text-properties '(line-spacing 0.25 line-height 1.25))
(set-frame-font "PragmataPro Mono Liga 18" nil t)

(provide 'my-fonts)

;; Local Variables:
;; no-byte-compile: t
;; no-native-compile: t
;; no-update-autoloads: t
;; End:

I found that the above works OK on macOS, but the commented part:

(use-package ligature
  :ensure (:host github
           :repo "mickeynp/ligature.el"
           :depth 1))

(add-to-list 'load-path (expand-file-name "~/pragmatapro/"))
(require 'pragmatapro-prettify-symbols-v0.830)

(defun conditionally-enable-pragmata-prettify ()
  "Enable `pragmata-prettify' only on GUI frames."
  (if (display-graphic-p)
      (progn
        (add-hook 'prog-mode-hook 'pragmata-prettify)
        (add-hook 'org-mode-hook 'pragmata-prettify)
        (ligature-mode 1)
        (global-prettify-symbols-mode 1))
    (remove-hook 'prog-mode-hook 'pragmata-prettify)
    (remove-hook 'org-mode-hook 'pragmata-prettify)
    (ligature-mode -1)
    (global-prettify-symbols-mode -1)))

(add-hook 'after-make-frame-functions #'conditionally-enable-pragmata-prettify)
(add-hook 'window-setup-hook #'conditionally-enable-pragmata-prettify)

to work better on Windows. This also works fine on macOS, but in that OS, it’s simpler to use only 1 package, instead of relying on the external file pragmatapro-prettify-symbols-v0.830.el too.

The prettifying process is too heavy to work nicely on the terminal (that’s what I’ve found myself, maybe I’m doing it wrong…). And, if I use a terminal capable of rendering ligatures (e.g. WezTerm, kitty), then I get ligatures in the terminal as well 🖋️!

my/my-theme.el

I used several themes with Emacs. I like leuven, dracula themes, monokai variants, but at this moment I’m using modus-themes:

;; -*- lexical-binding: t; -*-

(use-package modus-themes
  :ensure (:host github
           :repo "protesilaos/modus-themes"
           :depth 1)
  :config
  ;; Add all your customizations prior to loading the themes
  (setq modus-themes-italic-constructs t
        modus-themes-bold-constructs t
        modus-themes-disable-other-themes t

        modus-themes-completions
        '((matches . (extrabold))
          (selection . (semibold italic text-also)))

        modus-themes-org-blocks 'gray-background)

  ;; Maybe define some palette overrides, such as by using our presets
  (setq modus-themes-common-palette-overrides
        modus-themes-preset-overrides-intense)

  ;; Load the theme of your choice.
  ;; (load-theme 'modus-operandi-deuteranopia :no-confirm)
  (load-theme 'modus-operandi :no-confirm)
  (define-key global-map (kbd "<f5>") #'modus-themes-toggle))

(provide 'my-theme)

;; Local Variables:
;; no-byte-compile: t
;; no-native-compile: t
;; no-update-autoloads: t
;; End:

Date
August 4, 2024