2021-11-02 00:27:14 +00:00
#+TITLE : My Emacs Bootstrap
#+AUTHOR : Howard X. Abrams
#+DATE : 2021-10-08
#+FILETAGS : :emacs:
A literate programming file for bootstraping my Emacs Configuration.
#+BEGIN_SRC emacs-lisp :exports none
2022-03-09 18:45:37 +00:00
;;; bootstrap.el --- file for bootstraping my Emacs Configuration
;;
;; © 2021-2022 Howard X. Abrams
;; This work is licensed under a Creative Commons Attribution 4.0 International License.
;; See http://creativecommons.org/licenses/by/4.0/
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams >
;; Maintainer: Howard X. Abrams
;; Created: October 8, 2021
;;
;; This file is not part of GNU Emacs.
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; ~/other/hamacs/bootstrap.org
;; And tangle the file to recreate this one.
;;
;;; Code:
2021-11-02 00:27:14 +00:00
#+END_SRC
* Introduction
This file contains all the variable definitions and library loading for the other files in my project.
** Straight Package Installer
2022-03-03 23:16:50 +00:00
I'm going to be installing everything using the [[https://github.com/raxod502/straight.el#getting-started ][straight.el ]] for package installation and management. However, before I could tangle these org files, I needed to have =straight= grab the latest =org= , so the following initialization code is actually in [[file:initialize ][initialize ]], but the good stuff is:
2021-11-02 00:27:14 +00:00
2022-03-03 23:16:50 +00:00
#+BEGIN_SRC emacs-lisp :tangle no
(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))
2021-11-02 00:27:14 +00:00
#+END_SRC
Let's get the Straight project working with =use-package= :
2022-03-03 23:16:50 +00:00
#+BEGIN_SRC emacs-lisp :tangle no
2021-11-02 00:27:14 +00:00
(straight-use-package 'use-package)
#+END_SRC
2022-03-03 23:16:50 +00:00
2021-11-02 00:27:14 +00:00
While that enables the =:straight t= extension to =use-package= , let's just have that be the default:
2022-03-03 23:16:50 +00:00
#+BEGIN_SRC emacs-lisp :tangle no
2021-11-02 00:27:14 +00:00
(use-package straight
2021-11-12 04:59:22 +00:00
:custom (straight-use-package-by-default t
straight-default-vc 'git))
2021-11-02 00:27:14 +00:00
#+END_SRC
See the details in [[https://dev.to/jkreeftmeijer/emacs-package-management-with-straight-el-and-use-package-3oc8 ][this essay ]].
2021-12-08 21:56:30 +00:00
** OS Path and Native Compilation
2021-12-13 18:45:32 +00:00
Helper functions to allow code for specific operating systems:
#+BEGIN_SRC emacs-lisp
(defun ha-running-on-macos? ()
"Return non-nil if running on Mac OS systems."
(equal system-type 'darwin))
(defun ha-running-on-linux? ()
"Return non-nil if running on Linux systems."
(equal system-type 'gnu/linux))
#+END_SRC
2021-12-08 21:56:30 +00:00
With the way I start Emacs, I may not have the PATH I /actually/ use (from the shell) available, so we'll force it (code taken [[https://www.emacswiki.org/emacs/ExecPath ][from here ]]):
#+BEGIN_SRC emacs-lisp
(defun set-exec-path-from-shell ()
"Set up Emacs' `exec-path' and PATH environment variable to match
that used by the user's shell.
This is particularly useful under Mac OS X and macOS, where GUI
apps are not started from a shell."
(interactive)
2022-01-03 06:42:22 +00:00
(let* ((path-from-shell (shell-command-to-string "echo $PATH"))
(trimmed-path (replace-regexp-in-string (rx (zero-or-more space) eol)
"" path-from-shell))
(in-fish? (string-match (rx "fish" eol)
(shell-command-to-string "echo $SHELL")))
(separator (if in-fish? " " ":"))
(env-path (if in-fish? (replace-regexp-in-string " " ":" trimmed-path) trimmed-path)))
(message "PATH=%s" path-from-shell)
(setenv "PATH" env-path)
(setq exec-path (split-string trimmed-path separator))))
2021-12-08 21:56:30 +00:00
#+END_SRC
Clear up a Mac-specific issue that sometimes arises since I'm switching to [[http://akrl.sdf.org/gccemacs.html ][native compilation project ]], as the =Emacs.app= that I use doesn't have its =bin= directory, e.g. =Emacs.app/Contents/MacOS/bin= :
#+BEGIN_SRC emacs-lisp
2021-12-13 18:45:32 +00:00
(when (ha-running-on-macos?)
2022-01-03 06:42:22 +00:00
(add-to-list 'exec-path "/usr/local/bin")
2021-12-08 21:56:30 +00:00
(add-to-list 'exec-path (concat invocation-directory "bin") t))
#+END_SRC
Getting tired off all the packages that I load spewing a bunch of warnings that I can't do anything about:
#+BEGIN_SRC emacs-lisp
2021-12-13 18:45:32 +00:00
(when (and (fboundp 'native-comp-available-p)
(native-comp-available-p))
(setq native-comp-async-report-warnings-errors nil
native-comp-deferred-compilation t))
2021-12-08 21:56:30 +00:00
#+END_SRC
2022-01-03 06:42:22 +00:00
** GNU Pretty Good Privacy
On Linux, GPG is pretty straight-forward, but on the Mac, I often have troubles doing:
#+BEGIN_SRC sh
brew install gpg
#+END_SRC
And I need to install the [[https://gpgtools.org/ ][GPG Suite ]]. While very nice, isn't a GUI I really need, but can take advantage when installed:
#+BEGIN_SRC emacs-lisp
(defun executable (path)
"Return PATH if it is executable, see `file-executable-p'."
(when (file-executable-p path) path))
(use-package epa-file
:straight (:type built-in)
:custom (epg-gpg-program (or (executable "/usr/local/MacGPG2/bin/gpg")
(executable "/usr/local/bin/gpg")
2022-01-07 01:01:08 +00:00
(executable "/usr/local/opt/gpg")
(executable "/usr/bin/pgp")))
2022-01-03 06:42:22 +00:00
:config (epa-file-enable))
#+END_SRC
2021-11-02 00:27:14 +00:00
** Basic Libraries
The following packages come with Emacs, but seems like they still need loading:
#+BEGIN_SRC emacs-lisp
2022-01-06 23:36:39 +00:00
(use-package cl-lib
:straight (:type built-in)
:init (defun first (elt) (car elt))
:commands (first))
(require 'subr-x)
2021-11-06 00:06:55 +00:00
#+END_SRC
2022-01-06 23:36:39 +00:00
Ugh. Why am I getting a missing =first= function error? I define a simple implementation, that the CL library will overwrite ... at some point.
2021-11-02 00:27:14 +00:00
While most libraries will take care of their dependencies, I want to install /my dependent libraries/ . Especially, [[https://github.com/magnars/.emacs.d/ ][Magnar Sveen ]]'s Clojure-inspired [[https://github.com/magnars/dash.el ][dash.el ]] project:
#+BEGIN_SRC emacs-lisp
(use-package dash)
#+END_SRC
2022-01-06 23:36:39 +00:00
Sure this package is essentially syntactic sugar, and to help /share/ my configuration, I attempt to use =thread-last= instead of =->>= , but, I still like it.
2021-11-02 00:27:14 +00:00
The [[https://github.com/magnars/s.el ][s.el ]] project is a simpler string manipulation library that I (and other projects) use:
#+BEGIN_SRC emacs-lisp
(use-package s)
#+END_SRC
Manipulate file paths with the [[https://github.com/rejeep/f.el ][f.el ]] project:
#+BEGIN_SRC emacs-lisp
(use-package f)
#+END_SRC
** My Code Location
Much of my more complicated code comes from my website essays and other projects. The destination, however, shows up here:
#+BEGIN_SRC emacs-lisp
(add-to-list 'load-path (f-expand "~/.emacs.d/elisp"))
#+END_SRC
Hopefully, this will tie me over while I transition.
2021-11-06 00:06:55 +00:00
** Emacs Server Control
Sure the Emacs application will almost always have the =server-start= going, however, I need to control it just a bit (because I often have two instances running on some of my machines). What /defines/ the Emacs instance for work changes ... often:
#+BEGIN_SRC emacs-lisp
2022-01-03 06:42:22 +00:00
(defun ha-emacs-for-work? ()
"Return non-nil when the Emacs application's location matches as one for work.
2022-04-01 18:33:24 +00:00
Currently, this is the `emacs-plus@28' app that I have built with
2022-01-03 06:42:22 +00:00
the native-comp model, but I reserve the right to change this."
(and (f-dir? "~/work")
2022-04-01 18:33:24 +00:00
(string-match "emacs-plus@28" exec-directory)))
2021-11-06 00:06:55 +00:00
#+END_SRC
2022-04-01 18:33:24 +00:00
And now start the server with an appropriate tag name:
2021-11-06 00:06:55 +00:00
#+BEGIN_SRC emacs-lisp
2022-01-03 06:42:22 +00:00
(if (not (ha-emacs-for-work?))
(setq server-name "personal")
2021-11-06 00:06:55 +00:00
(setq server-name "work")
2022-01-03 06:42:22 +00:00
(when (ha-running-on-macos?)
(set-exec-path-from-shell)))
2021-11-06 00:06:55 +00:00
2022-01-03 06:42:22 +00:00
(server-start)
2021-11-06 00:06:55 +00:00
#+END_SRC
2021-11-02 00:27:14 +00:00
* Load the Rest
The following loads the rest of my org-mode literate files. I add them as they are /ready/ , but eventually, I'll trim this up into a nicer pattern.
#+BEGIN_SRC emacs-lisp
2021-11-29 22:01:05 +00:00
(defvar ha-hamacs-files (flatten-list `("ha-private.org"
2021-11-18 20:12:19 +00:00
"ha-config.org"
2021-11-14 06:14:55 +00:00
,(when (display-graphic-p)
"ha-display.org")
2021-11-10 01:18:52 +00:00
"ha-org.org"
2021-11-14 06:14:55 +00:00
,(when (display-graphic-p)
"ha-org-word-processor.org")
2021-11-10 01:18:52 +00:00
"ha-org-clipboard.org"
"ha-capturing-notes.org"
2021-11-15 04:42:15 +00:00
"ha-agendas.org"
2021-11-16 00:18:31 +00:00
"ha-passwords.org"
2021-11-10 01:49:52 +00:00
"ha-remoting.org"
2021-11-18 20:12:19 +00:00
"ha-programming.org"
"ha-programming-python.org"
2022-03-03 23:08:09 +00:00
,(if (ha-emacs-for-work?)
'("ha-org-sprint.org" "ha-work.org")
;; Personal Editor
2021-11-29 22:01:05 +00:00
'("ha-org-journaling.org"
2022-03-03 23:08:09 +00:00
"ha-irc.org"
"ha-org-publishing.org"
"ha-email.org"
"ha-programming-scheme.org"
"ha-aux-apps.org"
"ha-feed-reader.org"))))
2021-11-10 01:18:52 +00:00
"List of org files that complete the hamacs project.")
#+END_SRC
We can test/debug/reload any individual file, via:
#+BEGIN_SRC emacs-lisp
(defun ha-hamacs-load (file)
"Load or reload an org-mode FILE containing literate Emacs configuration code."
(interactive (list (completing-read "Org file: " ha-hamacs-files)))
2021-11-18 20:12:19 +00:00
(let ((full-file (f-join hamacs-source-dir file)))
(when (f-exists? full-file)
(org-babel-load-file full-file))))
2021-11-02 00:27:14 +00:00
#+END_SRC
2021-11-10 01:18:52 +00:00
And we can now load everything:
#+BEGIN_SRC emacs-lisp
2021-11-24 00:29:52 +00:00
(defun ha-hamacs-reload-all ()
"Reload our entire ecosystem of configuration files."
(interactive)
(dolist (file ha-hamacs-files)
(ha-hamacs-load file)))
#+END_SRC
And do it:
#+BEGIN_SRC emacs-lisp
(ha-hamacs-reload-all)
2021-11-02 00:27:14 +00:00
#+END_SRC
* Technical Artifacts :noexport:
Let's provide a name so that the file can be required:
#+BEGIN_SRC emacs-lisp :exports none
(provide 'bootstrap)
;;; bootstrap.el ends here
#+END_SRC
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
#+DESCRIPTION : A literate programming file for bootstrapping my environment.
#+PROPERTY : header-args:sh :tangle no
#+PROPERTY : header-args:emacs-lisp :tangle yes
#+PROPERTY : header-args :results none :eval no-export :comments no mkdirp yes
#+OPTIONS : num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
#+OPTIONS : skip:nil author:nil email:nil creator:nil timestamp:nil
#+INFOJS_OPT : view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js