Startup

Initial set-up

We are bootstrapping to some new server and want to stay as self-contained in the .emacs.d directory as possible.

;; we want to replace all calls to `user-emacs-directory' with `initel-directory'
(setq initel-directory (file-name-directory (or load-file-name buffer-file-name)))

I carry around two laptops: My main machine runs Linux and my secondary machine runs Windows. I need some mechanism to distinguish between the two platforms

(defconst on-windows (string-equal system-type "windows-nt"))

I pull packages from a server under my control which mirrors the ELPA/MELPA/whatever packages I use in my config. To keep others from accessing it, we keep access password protected. To download from a password-protected ELPA archive, we need to set the appropriate HTTP Basic Auth headers in the call to `url-retrieve`. This looks like the following. This is not the username, password, or server that is actually used.

(defconst do.bootstrap/archive-url "https://config.example.com/elpa/"
  "URL to my personal package repository")
(defconst do.bootstrap/archive-user "username"
  "Username for my personal package repository")
(defconst do.bootstrap/archive-password "password"
  "Password for my personal package repository")

We now construct the HTTPS Basic Auth headers for this username/password combination.

(defconst do.bootstrap/archive-auth-str
  (cons "Authorization"
        (concat "Basic " (base64-encode-string
                          (concat do.bootstrap/archive-user ":"
                                  do.bootstrap/archive-password))))
  "HTTPS Basic Auth string for my personal package repository")

Set up `package.el' to pull from my server.

(require 'package)
(setq package-user-dir (concat initel-directory "elpa"))
(setq package-native-compile t)
(unless on-windows ;; https://lists.gnu.org/archive/html/help-gnu-emacs/2019-08/msg00106.html
  (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
(setq package-archives `(("dennis" . ,do.bootstrap/archive-url)))

`package.el' uses `url.el' to retrive the ELPA packages. It used to be that we could give it a URL like https://username:[email protected] and everything works. Unfortunately, this hack seems to have broken in Emacs 29, forcing us to do this the "right" way. So we now advise `url-retrieve' to set `url-request-extra-headers' if the archive URL `do.bootstrap/archive-url' is being requested. This is not necessarily great, but that's what it is.

(defun do.bootstrap/url-retrieve-check-for-archive (orig-fun &rest args)
  "Like `url-retrive', but check for `do.bootstrap/archive-url'
first. If URL starts with that, add the appropriate username and
password as extra HTTP headers."
  ;; the `url' argument is the first argument to the original function
  (if (string-prefix-p do.bootstrap/archive-url (car args))
      ;; if we are requesting from my archive, add the basic auth headers
      (let ((url-request-extra-headers (cons do.bootstrap/archive-auth-str url-request-extra-headers)))
        (apply orig-fun args))
    ;; else just call `url-retrieve'
    (apply orig-fun args)))

;; advise `url-retrieve'
(advice-add 'url-retrieve :around #'do.bootstrap/url-retrieve-check-for-archive)

Now we can finally initialize `package.el' . Now, everytime `use-package' goes out to pull a package from my server, the HTTP Basic Auth headers will be set automatically.

(package-initialize)

Layers and themes

;; We enable or disable one of or several "layers", similar to how spacemacs
;; handles its configuration. Defaults to
(defvar do.layers/enabled-layers
  '(minimal faces org lsp terminal snippets mode-line completion
    cpp latex octave scheme julia python matlab misc mail)
  "A list of the enabled Layers for this configuration.

Currently available layers:

- minimal: The minimum viable configuration
- faces: A few tricks with faces, mainly for graphical Emacs
- org: `org-mode' customizations
- terminal: settings for the terminal environment (`ansi-term')
- snippets: `yasnippet' configuration
- completion: `company-mode' configuration
- mode-line: Custom mode-line
- mail: E-Mail configuration

- octave: GNU Octave settings
- cpp: C and C++ programming environment
- matlab: MATLAB programming environment
- julia: Julia programming environment
- python: Python programming environment
- scheme: Scheme programming environment
- latex: All things LaTeX (using AUCTeX)
- misc: Miscellaneous programming and text environments")

;; We enable one of our themes using this variable
(defvar do.theme/enabled-theme
  'dark
  "The choice of theme. The choices are:

- dark: Based on the internal `wombat-theme'
- light: Based on john2x's plan9-theme
- nil: No theme customizations at all")

Site File

;; We can customize the startup for a different machine by incluing elisp in
;; the special file "~/.emacs-site.el". This is loaded before any packages are
;; loaded and any variables set here might be overwritten. However, this can be
;; used to specify different themes or different layers for different machines,
;; for example.
(let ((site-file "~/.emacs-site.el"))
  (when (file-exists-p site-file)
    (load site-file)))
;; TODO make nicer
(let ((site-file "~/.emacs-site/emacs-site.el"))
  (when (file-exists-p site-file)
    (load site-file)))

use-package

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(setq use-package-verbose t)
(eval-when-compile
  (require 'use-package))
(use-package bind-key :ensure t :demand)
(use-package diminish :ensure t :demand)
(use-package dash
  :ensure t
  :demand
  :config
  (dash-enable-font-lock))
(use-package let-alist
  :ensure t
  :demand)

Exec path and other stuff

;; exec path
(unless on-windows
  (use-package exec-path-from-shell
    :ensure t
    :init
    (when (display-graphic-p)
      (exec-path-from-shell-initialize))))

;; Moved the custom.el stuff into its own file called ~/.emacs.d/customize.el
(setq custom-file (concat initel-directory "customize.el"))
(load custom-file)

;; Normally, I could use the =system-name= variable to get the current
;; hostname, but it seems to return the value of =hostname -f=, which is not
;; what I want. Therefore, I find the hostname manually by calling
;; =shell-command-to-string= and stripping some whitespace. This will probably
;; /not/ work on windows.
(let ((hn-executable "hostname"))
  (when (executable-find hn-executable)
    (setq hostname (->> hn-executable
      (shell-command-to-string)
      (replace-regexp-in-string "[ \t\n]*\\'" "")
      (replace-regexp-in-string "\\`[ \t\n]*" "")))))

;; ansi-term directory tracking breaks when "system-name" evaluates to
;; "<hostname>.Home". This is a dirty fix, but it works for now. Maybe
;; I can investigate further into this one day.
(setq system-name hostname)

Now we are ready to load the rest of the config.