Emacs Main Initialization File

This is the main file of my personnal Gnu/Emacs config environment. It is mainly use to reference other files, where all the magic happens.

The complete source code of my configuration files, including the Makefile, responsible of the org-to-el compilation, are available on its own git repository.

Global Options

Early init

To speed-up frame creation, the following lines go to the early-init.el.

(tool-bar-mode 0)
(menu-bar-mode 0)
(scroll-bar-mode 0)
(tooltip-mode 0)

;; Avoid blank screen, the following match dracula theme.
(when (display-graphic-p)
  (set-face-background 'default "#282a36" nil)
  (set-face-foreground 'default "#f8f8f2" nil))

;; Ignore .Xresources
(advice-add #'x-apply-session-resources :override #'ignore)

(setq frame-title-format "%b - Gnu/Emacs"
      icon-title-format "%b - Gnu/Emacs"
      inhibit-startup-screen t
      ;; Avoid useless leftover files at place I don't want.
      auto-save-default nil)

At various place in my init.el file I'll use another trick to quicken Emacs startup: I'll wrap big operation inside (let ((gc-cons-threshold most-positive-fixnum)) …) constructs to temporarily desactivate the garbage collector. After the end of the block, the default value (800ko) is reset.

Regular init

Then, we set the other options in the classical init.el file. These options are the bare minimal declarations to have a feel-good emacs.

(delete-selection-mode t)
(global-font-lock-mode t)
(blink-cursor-mode t)

(setq user-full-name "Étienne Deparis"
      select-enable-clipboard t
      visible-bell t
      current-language-environment "UTF-8"
      major-mode 'fundamental-mode ; To avoid all text-mode related autoloads
      ad-redefinition-action 'accept ; Remove useless warnings
      suggest-key-bindings 5
      backup-by-copying t
      delete-old-versions t)

(setq-default indent-tabs-mode nil
              show-trailing-whitespace t
              tab-width 4
              fill-column 78)

Package Management

Since the first decade of the XXIst century, we have a decent package manager for emacs. I use it to install a lot of supplementary features.

I only use melpa as an alternative package source. Since Emacs 27, the following declaration goes to the early-init.el file as packages are loaded before init.el is read.

(setq package-user-dir (expand-file-name "modes" user-emacs-directory)
      package-gnupghome-dir (expand-file-name "gnupg" package-user-dir)
      package-quickstart-file (expand-file-name "package-quickstart.el" package-user-dir)
      package-quickstart t)
(with-eval-after-load 'package
  (add-to-list 'package-archives
               '("melpa" . "https://melpa.org/packages/") t))

All the following packages need to be installed:

  • adoc-mode
  • ag
  • auctex
  • autopair
  • coffee-mode
  • company
  • company-auctex
  • company-inf-ruby
  • company-nginx
  • company-php
  • company-shell
  • dockerfile-mode
  • dracula-theme
  • editorconfig
  • elfeed
  • elfeed-protocol
  • elpher
  • emojify
  • encourage-mode
  • eww-lnum
  • flycheck
  • flycheck-color-mode-line
  • flycheck-package
  • fountain-mode
  • gemini-mode
  • hl-todo
  • htmlize
  • imenu-anywhere
  • json-navigator
  • lua-mode
  • magit
  • markdown-mode
  • multiple-cursors
  • org-clock-csv
  • package-lint
  • page-break-lines
  • php-mode
  • projectile
  • projectile-rails
  • rainbow-mode
  • robe
  • ruby-end
  • rust-mode
  • rvm
  • scss-mode
  • simple-mpc
  • slim-mode
  • smex
  • ttl-mode
  • web-mode
  • yaml-mode
  • yard-mode
  • zeal-at-point

For that, we use a custom script install_packages.el. This script stores the previous list in a variable called ed/packages-list. The content of this script is shown bellow:

(setq ed/packages-list '(adoc-mode ag auctex autopair coffee-mode company company-auctex company-inf-ruby company-nginx company-php company-shell dockerfile-mode dracula-theme editorconfig elfeed elfeed-protocol elpher emojify encourage-mode eww-lnum flycheck flycheck-color-mode-line flycheck-package fountain-mode gemini-mode hl-todo htmlize imenu-anywhere json-navigator lua-mode magit markdown-mode multiple-cursors org-clock-csv package-lint page-break-lines php-mode projectile projectile-rails rainbow-mode robe ruby-end rust-mode rvm scss-mode simple-mpc slim-mode smex ttl-mode web-mode yaml-mode yard-mode zeal-at-point))

(setq package-user-dir (expand-file-name "modes" user-emacs-directory)
      package-gnupghome-dir (expand-file-name "gnupg" package-user-dir)
      package-quickstart-file (expand-file-name "package-quickstart.el" package-user-dir)
      package-quickstart t)
(with-eval-after-load 'package
  (add-to-list 'package-archives
               '("melpa" . "https://melpa.org/packages/") t))

(defun ed/load-or-install-packages (&optional must-install-p)
  "Either install required packages or just load them.
If optional arg MUST-INSTALL-P is non-nil, this function will install
required packages. In the contrary, it will just initialize the
`package' library."
  (interactive)
  (let ((gc-cons-threshold most-positive-fixnum))
    (require 'package)
    (package-initialize must-install-p)
    (when must-install-p
      (package-refresh-contents)
      (dolist (package ed/packages-list)
        (package-install package))
      (package-quickstart-refresh))))

I use the org-mode tarball from the website. I've encounter some bug with their own repository, and no one with the tarball, thus I use this method to keep updated with the last version. I declare the path to the extracted package in the early-init.el file to avoid it to be poisonned by the Emacs embed one.

(add-to-list 'load-path (expand-file-name "org-mode/lisp" package-user-dir))
(with-eval-after-load 'org
  (let ((gc-cons-threshold most-positive-fixnum))
    (load-file (expand-file-name "org.elc" user-emacs-directory))))

I need to install these system package on archlinux to have the whole thing behaves as intended:

  • mu
  • xorg-fonts-100dpi
  • w3m (to have html mail preview in mu4e)

Temporary directory

Emacs packages and even emacs internal are used to store a lot of information at various places. I try to keep them together in a specific directory inside the emacs user directory.

We first need to be sure that temporary directory exists

(setq ed/temp-files-dir (expand-file-name "temp/" user-emacs-directory)
      ed/backup-dir (expand-file-name "backups/" ed/temp-files-dir))
(unless (file-exists-p ed/backup-dir)
  (make-directory ed/backup-dir t))

Some modes-specific cache files are declared with the rest of the mode configuration. The following variables are directly used by Emacs itself.

(setq tramp-persistency-file-name (expand-file-name "tramp.el" ed/temp-files-dir)
      bookmark-default-file (expand-file-name "bookmarks.el" ed/temp-files-dir)
      nsm-settings-file (expand-file-name "network-security.el" ed/temp-files-dir)
      abbrev-file-name (expand-file-name "abbrev.el" ed/temp-files-dir)
      save-place-file (expand-file-name "places.el" ed/temp-files-dir)
      custom-file (expand-file-name "custom.el" ed/temp-files-dir)
      eshell-directory-name (expand-file-name "eshell/" ed/temp-files-dir)
      backup-directory-alist `(("." . ,ed/backup-dir)
                               (,tramp-file-name-regexp nil))
      auto-save-file-name-transforms `((".*" ,ed/backup-dir t))
      auto-save-list-file-prefix ed/backup-dir)

Detailed Configuration

I split my detailed configuration into the following files, which contain all the required settings to customize various aspects of my emacs usage (color theme, authorship environment, integrated development environment…):

They do not contains any sensible information, in order to publish them widely on the internet. All my private stuff are kept in the file secrets/milouse.el.

We should not forgot to load the content of the previously declared custom-file, which contains all customizations made from Emacs.

Startup optimization

Declare a list of things to defer to quicken the Emacs startup. There are two lists: the first will be always run and the second will hold time consuming stuff only needed in a graphical environment (i.e. not to be used by emacs -nw). These lists will be populated during configuration files loading.

(defvar ed/call-me-maybe '())
(defvar ed/call-me-maybe-when-graphic '())

All my configuration files are loaded within a lambda function executed by the emacs-startup-hook in order to quicken Emacs startup. In this method, we use some with-eval-after-load to defer some more customizations, for which we can wait longer.

Finally, we unwrap the previous two call-me-maybe lists. We begin by including files we always need, even in a terminal, with the after-init-hook, which is always run, even with -nw argument.

(add-hook 'after-init-hook #'(lambda ()
  (let ((gc-cons-threshold most-positive-fixnum))
    (load custom-file) ;; Load emacs custom file
    (load-file (expand-file-name "beauty.elc" user-emacs-directory))
    (load-file (expand-file-name "utils.elc" user-emacs-directory))
    (load-file (expand-file-name "various-comp-lang.elc" user-emacs-directory))
    ;; Load my secrets
    (let ((secret-file (expand-file-name "secrets/milouse.el" user-emacs-directory)))
      (when (file-exists-p secret-file)
        (load-file secret-file)
        ;; Only load mu4e if my secret file exists, as it is a hard
        ;; requirements for mu.
        (load-file (expand-file-name "mu4e-autoload.elc" user-emacs-directory)))))
  ;; I can now safely activate back auto-saving-mode
  (setq auto-save-default t)))

Then we include stuff only required in a graphical environment, as emacs-startup-hook is only run when a window system is present.

(add-hook 'emacs-startup-hook #'(lambda ()
  ;; Unwrap deferred calls
  (run-with-idle-timer 2 nil #'(lambda ()
    (let ((gc-cons-threshold most-positive-fixnum))
      (with-temp-message "Here's my number...So call me maybe"
        (mapc 'eval ed/call-me-maybe)
        (when (display-graphic-p)
          (mapc 'eval ed/call-me-maybe-when-graphic))
        (message "Here's my number...So call me maybe...done")))))))