No description
Find a file
2026-01-03 18:11:19 +01:00
.gitignore Initial check in 2026-01-03 18:06:01 +01:00
boot.el Initial check in 2026-01-03 18:06:01 +01:00
boot.sh Initial check in 2026-01-03 18:06:01 +01:00
readme.org User interface settings and ModeLine 2026-01-03 18:11:19 +01:00

Emacs configuration

Preface

GNU Emacs isn't a text editor, it's the ultimate productivity environment for hackers. This document contains the core parts of my Emacs configuration which drive the workflow I use every day.

This Emacs configuration file does use setup.el for package management.

Default themes and faces

Default theme. It is needed by early-init.el too. In this case we have nice themed emacs even only early-init does exist.

Face extensions

Common face definitions

  ;;; -*- lexical-binding: t -*-
  ;;; Do not edit by hand! It is generated from readme.org.
  
  ;; -------------------------------------------------------------------------------------------------
  ;; common face definitions
  ;; -------------------------------------------------------------------------------------------------
  (defface mr:operator-face
    '((t (:foreground "red")))
    "Face used to highlight operator characters like +, -"
    :group 'mr:faces)

  (defface mr:relop-face
    '((t (:foreground "dodgerblue1")))
    "Face used to highlight relational operator characters like <, >="
    :group 'mr:faces)

  (defface mr:paren-face
    '((t (:foreground "indianred")))
    "Face used to highlight parentheses characters like (, ]"
    :group 'mr:faces)

  (defvar mr:operator-face    'mr:operator-face   )
  (defvar mr:relop-face       'mr:relop-face      )
  (defvar mr:paren-face       'mr:paren-face      )

Assembly mode face definitions

  ;; -------------------------------------------------------------------------------------------------
  ;; assembly mode face definitions
  ;; -------------------------------------------------------------------------------------------------
  (defface mr:asm-comment2-face
    '((t (:foreground "gray50")))
    "Face used to highlight comments beginnig with ;;"
    :group 'mr:faces)

  (defface mr:asm-comment3-face
    '((t (:foreground "gray50")))
    "Face used to highlight comments beginnig with ;;;"
    :group 'mr:faces)

  (defface mr:asm-glabel-face
    '((t (:foreground "sienna3")))
    "Face used to highlight global labels"
    :group 'mr:faces)

  (defface mr:asm-label-face
    '((t (:foreground "sienna1")))
    "Face used to highligh labels"
    :group 'mr:faces)

  (defface mr:asm-delim-face
    '((t (:foreground "firebrick")))
    "Face used to highlight delimiters"
    :group 'mr:faces)

  (defface mr:asm-inst-face
    '((t (:foreground "DodgerBlue3")))
    "Face used to highlight mnemonics"
    :group 'mr:faces)

  (defface mr:asm-reg-face
    '((t (:foreground "DeepSkyBlue3")))
    "Face used to highlight register names"
    :group 'mr:faces)

  (defface mr:asm-dir-face
    '((t (:foreground "brown")))
    "Face used to highlight assembler directives"
    :group 'mr:faces)

  (defface mr:asm-op-face
    '((t (:foreground "firebrick")))
    "Face used to highlight operators"
    :group 'mr:faces)

  (defface mr:asm-op2-face
    '((t (:foreground "CadetBlue3")))
    "Face used to highlight text operators"
    :group 'mr:faces)

  (defvar mr:asm-comment2-face  'mr:asm-comment2-face   )
  (defvar mr:asm-comment3-face  'mr:asm-comment3-face   )
  (defvar mr:asm-glabel-face    'mr:asm-glabel-face     )
  (defvar mr:asm-label-face     'mr:asm-label-face      )
  (defvar mr:asm-delim-face     'mr:asm-delim-face      )
  (defvar mr:asm-inst-face      'mr:asm-inst-face       )
  (defvar mr:asm-reg-face       'mr:asm-reg-face        )
  (defvar mr:asm-dir-face       'mr:asm-dir-face        )
  (defvar mr:asm-op-face        'mr:asm-op-face         )
  (defvar mr:asm-op2-face       'mr:asm-op2-face        )

Provide face

  (provide 'r2849-faces)

Default theme

    ;;; -*- lexical-binding: t -*-
    ;;; Do not edit by hand! It is generated from readme.org.

  (deftheme r2849
    "Created 2023-04-13.")

  (custom-theme-set-variables
   'r2849
   '(inhibit-startup-message t)
   '(scroll-step 1)
   '(truncate-lines t)
   '(tool-bar-mode nil)
   '(scroll-bar-mode nil)
   '(column-number-mode t)
   '(size-indication-mode t)
   '(display-time-24hr-format t)
   '(display-time-day-and-date t)
   '(tab-always-indent nil)
   '(scroll-bar-mode nil)
   '(visible-bell nil))

  (custom-set-faces
   '(default
     ((t
       (
        :inherit     nil
        :family     "RobotoMono Nerd Font Mono"
        :foundry    "monotype"
        :stipple    nil
        :background "black"
        :foreground "gray70"
        :height     140
        :weight     normal
        ))))

   ;; org mode faces
   '(org-document-title  ((t (:height 1.50 :bold t   :italic nil ))))
   '(org-level-1         ((t (:height 1.20 :bold t   :italic nil ))))
   '(org-level-2         ((t (:height 1.10 :bold nil :italic t   ))))
   '(org-level-3         ((t (:height 1.05 ))))
   '(org-level-4         ((t (:height 1.00 ))))
   '(org-level-5         ((t (:height 1.00 ))))
   '(org-level-6         ((t (:height 1.00 ))))
   '(org-level-7         ((t (:height 1.00 ))))
   '(org-level-8         ((t (:height 1.00 ))))


   ;; modeline
   '(mode-line-emphasis  ((t (:italic t :bold nil))))

   ;; doom modeline
   '(doom-modeline-persp-name ((t (:inherit mode-line-emphasis :foreground "dark orange" :italic t))))
 
   ;; common progmode faces
   '(font-lock-comment-face          ((t (:foreground "gray50"                               ))))
   '(font-lock-function-name-face    ((t (:foreground "deep pink"                            ))))
   '(font-lock-keyword-face          ((t (:foreground "dark orange"                          ))))
   '(font-lock-string-face           ((t (:foreground "dark sea green"                       ))))

   ;; current line and line number faces
   '(line-number-current-line        ((t (:foreground "white"    :italic nil :bold nil       ))))
   '(line-number                     ((t (:foreground "gray50"   :italic nil :bold nil       ))))
   '(hl-line                         ((t (:background "gray10"                               )))) 
   )

  ;; Local Variables:
  ;; no-byte-compile: t
  ;; End:

  (provide-theme 'r2849)

Early init

Most customizations for Emacs should be put in the normal init file. However, it is sometimes necessary to have customizations take effect during Emacs startup earlier than the normal init file is processed. Such customizations can be put in the early init file. This file is loaded before the package system and GUI is initialized, so in it you can customize variables that affect the package initialization process, such as package-enable-at-startup, package-load-list, and package-user-dir.

  ;;; -*- lexical-binding: t -*-
  ;;; Do not edit by hand! It is generated from readme.org.

  ;;; Disable package.el in favor of straight.el
  ;;; Do not allow loading from the package cache.
  (setq package-enable-at-startup nil)
  (setq package-quickstart        nil)

  ;;; Make startup faster by reducing the frequency of garbage collection and then use a hook to
  ;;; measure Emacs startup time. After init, restore the default garbage collection parameters.
  (let ((default-gc-threshold  gc-cons-threshold)
        (default-gc-percentage gc-cons-percentage))
    (setq gc-cons-threshold most-positive-fixnum
          gc-cons-percentage 0.8)
    (add-hook 'after-init-hook
              (lambda ()
                (setq gc-cons-percentage default-gc-percentage
                      gc-cons-threshold  default-gc-threshold))))

  ;;; profile emacs startup
  (add-hook 'emacs-startup-hook
            (lambda ()
              (message "*** Emacs loaded in %s seconds with %d garbage collections."
                       (emacs-init-time "%.2f")
                       gcs-done)))

  ;;; Prevent unwanted runtime builds in gccemacs (native-comp); packages are compiled
  ;;; ahead-of-time when they are installed and site files are compiled when gccemacs
  ;;; is installed.
  (setq comp-deferred-compilation nil)

  ;;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early.
  (push '(menu-bar-lines . 0)     default-frame-alist)
  (push '(tool-bar-lines . 0)     default-frame-alist)
  (push '(vertical-scroll-bars)   default-frame-alist)

  ;;; Resizing the Emacs frame can be a terribly expensive part of changing the font. By inhibiting 
  ;;; this, we easily halve startup times with fonts that are larger than the system default.
  (setq frame-inhibit-implied-resize t)

  ;;; disable GUI elements
  (menu-bar-mode      -1)
  (tool-bar-mode      -1)
  (scroll-bar-mode    -1)

  (setq inhibit-splash-screen t)
  (setq use-file-dialog nil)

  ;;; Default coding system
  (prefer-coding-system           'utf-8)
  (set-default-coding-systems     'utf-8)
  (set-terminal-coding-system     'utf-8)
  (set-keyboard-coding-system     'utf-8)
  (setq buffer-file-coding-system 'utf-8)

  ;;; Default frame size
  (add-to-list 'default-frame-alist '(height .  45))
  (add-to-list 'default-frame-alist '(width  . 160))

  ;;; Include paths
  (add-to-list 'load-path (substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp"           ))
  (add-to-list 'load-path (substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp/themes"    ))
  (add-to-list 'load-path (substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp/progmodes" ))

  ;;; Theme paths
  (add-to-list 'custom-theme-load-path 
               (substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp/themes"))

  ;;; tab-bar-mode is incompatible with perspective (as of 2023-05) so do not enable it.
  ;;; (setq tab-bar-format '(tab-bar-format-global))
  ;;; (tab-bar-mode)

  (setq tab-width 4)

  ;;; Do not appear a bunch of transient files as untracked in the Git/Plastic repo so
  ;;; move them all to another location.
  ;;;
  ;;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d
  (setq user-emacs-directory (expand-file-name "~/.cache/emacs/")
        url-history-file (expand-file-name "url/history" user-emacs-directory))

  ;;; Set the cache folder for native compiled files.
  (when (boundp 'native-comp-eln-load-path)
    (startup-redirect-eln-cache (expand-file-name "eln-cache" user-emacs-directory)))

  ;;; Bootstrap feature =straight= to install no-littering which is needed to keep clean .emacs.d
  ;;; It should install itself into the redefined user-emacs-directory !

  (unless (featurep 'straight)
    ;; Bootstrap straight.el
    (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)))

  ;;; Use straight.el for use-package expressions
  (straight-use-package 'use-package)

  ;;; Use no-littering to automatically set common paths to the new user-emacs-directory.
  ;;; Load the feature =no-littering= as early as possible. Make sure it is loaded at least
  ;;; before any path variables are changed using some other methods.
  (straight-use-package 'no-littering)
  (require 'no-littering)

  ;;; Customizations must be in a separate file not in the user initialization script (init.el).
  (setq custom-file (locate-user-emacs-file "custom.el"))
  (load custom-file 'noerror 'nomessage)

  ;;; Load theme as soon as possible
  (setq custom-safe-themes t)
  (load-theme 'r2849)

Streamlined configuration with setup.el

Use the excellent setup.el by pkal as an alternative to use-package.

  (straight-use-package
   '(setup
        :type git
        :host nil
        :repo "https://git.sr.ht/~pkal/setup"))

  (require 'setup)

  ;; Uncomment this for debugging purposes
  ;; (defun dw/log-require (&rest args)
  ;;   (with-current-buffer (get-buffer-create "*require-log*")
  ;;     (insert (format "%s\n"
  ;;                     (file-name-nondirectory (car args))))))
  ;; (add-to-list 'after-load-functions #'dw/log-require)

:straight

StraightEl (on GitHub) is a “next-generation, purely functional package manager for the Emacs hacker,” according to its repo. I like using it is all I know. Heres a :straight keyword for setup:

  (setup-define :straight
	(lambda (recipe)
    `(unless (straight-use-package ',recipe)
       ,(setup-quit)))
  :documentation
  "Install RECIPE with `straight-use-package'.
This macro can be used as HEAD, and will replace itself with the
first RECIPE's package."
  :repeatable t
  :shorthand (lambda (sexp)
               (let ((recipe (cadr sexp)))
                 (if (consp recipe)
                     (car recipe)
                   recipe))))

:pkg

  (defun mr/filter-straight-recipe (recipe)
    (let* ((plist (cdr recipe))
           (name (plist-get plist :straight)))
      (cons (if (and name (not (equal name t)))
                name
              (car recipe))
            (plist-put plist :straight nil))))

  (setup-define :pkg
    (lambda (&rest recipe)
      `(straight-use-package ',(mr/filter-straight-recipe recipe)))
    :documentation "Install RECIPE via Guix or straight.el"
    :shorthand #'cadr)

:delay

Delay the loading of a package until a certain amount of idle time has passed.

  (setup-define :delay
     (lambda (&rest time)
       `(run-with-idle-timer ,(or time 1)
                             nil ;; Don't repeat
                             (lambda () (require ',(setup-get 'feature)))))
     :documentation "Delay loading the feature until a certain amount of idle time has passed.")

:disabled

Used to disable a package configuration, similar to :disabled in use-package.

  (setup-define :disabled
    (lambda ()
      `,(setup-quit))
    :documentation "Always stop evaluating the body.")

:load-after

This keyword causes a body to be executed after other packages/features are loaded:

  (setup-define :load-after
      (lambda (features &rest body)
        (let ((body `(progn
                       (require ',(setup-get 'feature))
                       ,@body)))
          (dolist (feature (if (listp features)
                               (nreverse features)
                             (list features)))
            (setq body `(with-eval-after-load ',feature ,body)))
          body))
    :documentation "Load the current feature after FEATURES."
    :indent 1)

:face

Customizing faces.

  (setup-define :face
	(lambda (face spec)
	  `(custom-set-faces (quote (,face ,spec))))
	:documentation "Customize FACE to SPEC."
	:signature '(face spec ...)
	:debug '(setup)
	:repeatable t
	:after-loaded t)

Keyboard bindings

ESC cancels all

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

Let's Be Evil

Some tips can be found here:

  (setup (:straight undo-tree)
    (setq undo-tree-auto-save-history nil)
    (global-undo-tree-mode 1))

  (setup (:straight goto-chg))
  (setup (:straight evil)

    ;; Pre-load configuration
    (setq evil-want-integration t)
    (setq evil-want-keybinding nil)
    (setq evil-want-C-u-scroll t)
    (setq evil-want-C-i-jump nil)
    (setq evil-respect-visual-line-mode t)
    (setq evil-undo-system 'undo-tree)

    ;; Activate the Evil
    (evil-mode 1)

    ;; Set Emacs state modes
    (dolist (mode '(custom-mode
                    eshell-mode
                    git-rebase-mode
                    erc-mode
                    circe-server-mode
                    circe-chat-mode
                    circe-query-mode
                    sauron-mode
                    term-mode))
      (add-to-list 'evil-emacs-state-modes mode))

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

    (evil-set-initial-state 'messages-buffer-mode 'normal)
    (evil-set-initial-state 'dashboard-mode 'normal))

  (setup (:pkg evil-collection)
    ;; Is this a bug in evil-collection?
    (setq evil-collection-company-use-tng nil)
    (:load-after evil
      (:option evil-collection-outline-bind-tab-p nil
               (remove evil-collection-mode-list) 'lispy
               (remove evil-collection-mode-list) 'org-present)
      (evil-collection-init)))

Keybinding Panel (which-key)

which-key is great for getting an overview of what keybindings are available based on the prefix keys you entered. Learned about this one from Spacemacs.

  (setup (:pkg which-key)
    (:load-after diminish
      (diminish 'which-key-mode)
      (which-key-mode)
      (setq which-key-idle-delay 0.3)))

Simplify leader bindings (general.el)

general.el is a fantastic library for defining prefixed keybindings, especially in conjunction with Evil modes.

  (setup (:pkg general)
    (general-evil-setup t)

    (general-create-definer mr/leader-key
      :keymaps '(normal insert visual emacs)
      :prefix "SPC"
      :global-prefix "C-SPC")

    (general-create-definer mr/ctrl-c-keys
      :prefix "C-c"))

General configuration

User Interface

Clean up Emacs' user interface, make it more minimal. Improve scrolling.

  (setq mouse-wheel-scroll-amount '(3 ((shift) . 1))) ;; one line at a time
  (setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling
  (setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse
  (setq scroll-step 1) ;; keyboard scroll one line at a time
  (setq use-dialog-box nil) ;; Disable dialog boxes since they weren't working in Mac OSX

Set frame transparency (and maximize windows by default).

;  (set-frame-parameter (selected-frame) 'alpha '(90 . 90))
;  (add-to-list 'default-frame-alist '(alpha . (90 . 90)))
;  (set-frame-parameter (selected-frame) 'fullscreen 'maximized)
;  (add-to-list 'default-frame-alist '(fullscreen . maximized))

Set line spacing.

  
  (setq line-spacing 0.03)

Enable line numbers and customize their format (e.g.: relative numbers). Also set the width of the line number column.

  
  (column-number-mode)

  ;; Enable line numbers for some modes
  (dolist (mode '(text-mode-hook
                  prog-mode-hook
                  conf-mode-hook))
    (add-hook mode (lambda ()
                     (display-line-numbers-mode 1)
                     (setq display-line-numbers 'relative))))

  ;; Override some modes which derive from the above
  (dolist (mode '(org-mode-hook))
    (add-hook mode (lambda ()
                     (display-line-numbers-mode 1)
                     (setq display-line-numbers 'relative))))

  (setq-default display-line-numbers-width 4)

Don't warn for large files (shows up when launching videos) Don't warn for following symlinked files Don't warn when advice is added for functions

  (setq large-file-warning-threshold nil)
  (setq vc-follow-symlinks t)
  (setq ad-redefinition-action 'accept)

Theme

These days I bounce around between themes included with DOOM Themes since they're well-designed and integrate with a lot of Emacs packages.

A nice gallery of Emacs themes can be found at https://emacsthemes.com/.

Alternate themes:

  • doom-snazzy
  • doom-vibrant
  (setup (:pkg spacegray-theme))
  (setup (:pkg doom-themes))

  (add-hook 'emacs-startup-hook
			(lambda ()
;			  (load-theme 'doom-palenight 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)

Enable Mode Diminishing

The diminish package hides pesky minor modes from the modelines.

  (setup (:pkg diminish))

Doom Modeline

  ;; You must run (nerd-icons-install-fonts) one time after
  ;; installing this package!

  (setup (:straight minions)
    (:hook-into doom-modeline-mode))

  (setup (:pkg shrink-path))

  (setup (:pkg doom-modeline)
    (:hook-into after-init-hook)

    (:option doom-modeline-height 21
	     doom-modeline-bar-width 6
	     doom-modeline-lsp t
	     doom-modeline-github nil
	     doom-modeline-mu4e nil
	     doom-modeline-irc t
	     doom-modeline-modal-icon nil
	     doom-modeline-minor-modes t
	     doom-modeline-persp-name nil
	     doom-modeline-buffer-file-name-style 'truncate-except-project
	     doom-modeline-major-mode-icon nil)
    (:face mode-line          ((t (:height 0.95))))
    (:face mode-line-inactive ((t (:height 0.95)))))

					  ;   (setq doom-modeline-display-default-persp-name nil)
					  ;   (setq doom-modeline-persp-name nil)