Files
config.phundrak.com/docs/emacs/index.org
Lucien Cartier-Tilet 1ea379f907 docs(emacs): user user-lisp/ dir instead of plain lisp/ dir
Also precompile user-lisp content if the ELC files are older than
their original source file.
2026-05-03 15:48:58 +02:00

6.5 KiB
Raw Blame History

Emacs Configuration

Emacs Configuration

./img/emacs.svg

Introduction

After a couple of years using Spacemacs and a failed attempt at switching to DoomEmacs, Im finally switching back to a vanilla configuration! Why? Because I got tired of the framework getting in my way when I wanted to do stuff. Im sure this is more applicable to Spacemacs than DoomEmacs since the latter has nice macros written to easily add new packages and configure them, such as package!, after!, and others. But ultimately, I wanted to have a system I designed entirely, with the keybinds I want, the packages I want.

Also, why Emacs? You know this famous quote:

Emacs is a great operating system, it just lacks a good text editor.

Its actually pretty true in my opinion. Emacs is basically a Lisp machine with a default text editor, programmed with EmacsLisp, a general-purpose programming language. Therefore, if you want to do something in Emacs, with enough Elisp you can do it — if its not in Emacs already, that is.

Dammit Emacs…
XKCD n°378: Real Programmers

A Warning Before You Proceed

This configuration makes heavy use of the noweb syntax. This means if you encounter some code that looks <<like-this>>, org-mode will replace this snippet with another code snippet declared elsewhere in my configuration. If you see some code that looks <<like-this()>>, some generating code will run and replace this piece of text with the text generated. A quick example:

(defun hello ()
  <<generate-docstring()>>
  <<print-hello>>)

Will instead appear as

(defun hello ()
  <<generate-docstring()>>
  <<print-hello>>)

This is because I have the block of code below named generate-docstring which generates an output, which replaces its noweb tag. You can recognize noweb snippets generating code with the parenthesis. Often, such blocks arent visible in my HTML exports, but you can still see them if you open the actual org source file.

(concat "\""
        "Print \\\"Hello World!\\\" in the minibuffer."
        "\"")

On the other hand, noweb snippets without parenthesis simply replace the snippet with the equivalent named code block. For instance the one below is named print-hello and is placed as-is in the target source block.

(message "Hello World!")

Loading All Configuration Modules

Here are all my Emacs configuration modules, each dedicated to a specific aspect of my Emacs configuration.

Module Name Config Page
basic-config.el Basic Configuration
custom-elisp.el Custom Elisp
package-manager.el Package Manager
keybinding-managers.el Keybinding Managers
applications.el Packages — Applications
autocompletion.el Packages — Autocompletion
editing.el Packages — Editing
emacs-builtin.el Packages — Emacs Built-ins
helpful.el Packages — Making My Life Easier
latex.el Packages — LaTeX
misc.el Packages — Misc
org.el Packages — Org Mode
programming.el Packages — Programming
visual-config.el Packages — Visual Configuration
keybindings.el Keybindings
  • basic-config.el
  • custom-elisp.el
  • package-manager.el
  • keybinding-managers.el
  • applications.el
  • autocompletion.el
  • editing.el
  • emacs-builtin.el
  • helpful.el
  • latex.el
  • misc.el
  • org.el
  • programming.el
  • visual-config.el
  • keybindings.el

The first thing Emacs does is verify if any of these modules need to be byte-compiled again, i.e. if the .el file is younger than its corresponding .elc file. If so, the .el file is byte-compiled.

(defun my/mtime (file)
  "Get the modification time of FILE as a timestamp."
  (let* ((current-time-list nil)
         (attr (file-attributes file))
         (time-attr (file-attribute-modification-time attr)))
    (/ (car time-attr) (cdr time-attr))))

;; `modules' is a variable created by org-mode from the table of modules
(let* ((config-dir (expand-file-name "user-lisp" user-emacs-directory))
       (modules (mapcar (lambda (module)
                         (file-name-sans-extension (expand-file-name module config-dir)))
                        modules)))
  (dolist (module modules)
    (let* ((el-file (concat module ".el"))
           (elc-file (concat module ".elc"))
           (el-last-change (my/mtime el-file))
           (elc-last-change (if (file-exists-p elc-file) (my/mtime elc-file) 0)))
      (when (> el-last-change elc-last-change)
        (byte-compile-file el-file)))))

Now we can load them.

(dolist (module (mapcar #'file-name-sans-extension modules))
  (load (expand-file-name module
                          (expand-file-name "user-lisp" user-emacs-directory))))