From e88f4f5d8df6f16cdebf7aec7705848e86316540 Mon Sep 17 00:00:00 2001 From: Christopher James Hayward Date: Sun, 16 May 2021 17:07:24 -0400 Subject: [PATCH] Refactor initialization --- README.org | 156 ++++++++++++++++++++++++++++++++++++++++++++-- early-init.el | 119 +++++++++++++++++++++++++++++++++-- elisp/.git-keep | 0 elisp/cleanup.el | 34 ---------- elisp/hosts.el | 10 --- elisp/modules.el | 12 ---- elisp/options.el | 44 ------------- elisp/packages.el | 35 ----------- 8 files changed, 266 insertions(+), 144 deletions(-) delete mode 100644 elisp/.git-keep delete mode 100644 elisp/cleanup.el delete mode 100644 elisp/hosts.el delete mode 100644 elisp/modules.el delete mode 100644 elisp/options.el delete mode 100644 elisp/packages.el diff --git a/README.org b/README.org index 086fbc1..04fd0fb 100644 --- a/README.org +++ b/README.org @@ -2,7 +2,7 @@ #+AUTHOR: Christopher James Hayward #+EMAIL: chris@chrishayward.xyz -#+PROPERTY: header-args :results silent :eval no-export :comments org +#+PROPERTY: header-args :results silent :eval no-export #+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil #+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil @@ -25,9 +25,9 @@ Portable *GNU/Emacs*[fn:1] dotfiles. Built for Life, Liberty, and the Open Road. + 100% Literate * Setup -# :PROPERTIES: -# :header-args: :tangle early-init.el -# :END: +:PROPERTIES: +:header-args: :tangle early-init.el +:END: These is my personal configuration(s) for *GNU/Linux*[fn:2], and *GNU/Emacs*[fn:1] software. It enables a consistent experience and computing environment across all of my machines. The entire experience is controlled with *GNU/Emacs*[fn:1], leveraging it's capabilities for *Literate Programming*[fn:3]. @@ -36,6 +36,146 @@ These is my personal configuration(s) for *GNU/Linux*[fn:2], and *GNU/Emacs*[fn: ;; Please make any modifications there. #+end_src +** Cleanup + +Emacs creates a lot of files relative to ~user-emacs-directory~. These files are not part of this immutable configuration and do not belong in the Emacs directory. To solve this issue, before most of the packages have loaded, I change the value, a method I detail in this post[fn:4] + +#+begin_src emacs-lisp +;; The original value of `user-emacs-directory' prior to redirection. +(defconst dotfiles/home + (or (getenv "DOTFILES_HOME") + (expand-file-name user-emacs-directory))) + +;; The redirection target of `user-emacs-directory' during initialization. +(defconst dotfiles/cache + (or (getenv "DOTFILES_CACHE") + (expand-file-name "~/.cache/emacs"))) + +;; Make sure `dotfiles/cache' is a valid directory. +(unless (file-exists-p dotfiles/cache) + (make-directory dotfiles/cache t)) + +;; Redirect the value of `user-emacs-directory'. +(setq user-emacs-directory dotfiles/cache) + +;; Disable error messages for packages that don't support native-comp. +(setq comp-async-report-warnings-errors nil) + +;; Disable unwanted features. +(setq make-backup-files nil + create-lockfiles nil) +#+end_src + +** Packages + +Download and install packages using ~straight.el~[fn:5], a fully functional package manager for Emacs that integrates with ~use-package~, a popular way of installing packages for Emacs. + +#+begin_src emacs-lisp +;; Apply the configurations prior to bootstrapping the package manager. +(setq straight-repository-branch "master" + straight-use-package-by-default t + package-enable-at-startup nil) +#+end_src + +Bootstrap the package manager by loading the bootstrap-file, downloading it when it doesn't exist on a new system. This operation may take some time on a new system. + +#+begin_src emacs-lisp +;; Bootstrap the package manager. +(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)) + +;; Integrate with `use-package' by installing it via `straight'. +(straight-use-package 'use-package) + +;; Specify core package sources. +(straight-use-package 'no-littering) +(straight-use-package '(org :local-repo nil) +#+end_src + +** Options + +All of the options are defined early in the config, this is for two reasons: The first being to allow the possibility of sharing between modules, the values which are set early in the configuration. The second reason, related to the first, is to allow machine specific overrides depending on the context where Emacs is running. + +#+begin_src emacs-lisp +;; All of the modules available sorted in their default load order. +(defconst dotfiles/modules-p + '(org trash keys evil dired magit + shell mu4e elfeed eshell vterm + gpg pass x11 exwm roam agenda + spelling grammar reveal hugo + capture projects docker lsp dap + cc go uml conf python fonts ivy + themes modeline dashboard)) + +;; All of the enabled modules. +(defvar dotfiles/modules dotfiles/modules-p) + +;; The default system language. +(defvar dotfiles/language (getenv "LANG")) + +;; Configure a unified system font. +(defvar dotfiles/font "Fira Code") + +;; Default system font size. +(defvar dotfiles/font-size 96) + +;; Delay time before offering suggestions and completions. +(defvar dotfiles/idle 0.0) + +;; The all powerful leader key. +(defvar dotfiles/leader-key "SPC") + +;; Global prefix for the leader key under X11 windows. +(defvar dotfiles/leader-key-global + (concat "C-" dotfiles/leader-key)) + +;; The location on disk of source code projects. +(defvar dotfiles/projects + (or (getenv "DOTFILES_PROJECTS") + (expand-file-name "~/.local/source"))) + +;; The location on disk of the local copy of the password store. +(defvar dotfiles/passwords + (or (getenv "DOTFILES_PASSWORDS") + (expand-file-name "~/.password-store"))) + +;; The public GPG key to encrpyt files, and emails for / to / with. +(defvar dotfiles/public-key "37AB1CB72B741E478CA026D43025DCBD46F81C0F" +#+end_src + +** Methods + +Define two methods that will be used in the next phase of startup: The first method will load a machine-specific (host) configuration file, and the second method will load a custom module definition. + +#+begin_src emacs-lisp +;; Load a host configuration. +(defun dotfiles/load-host (host-name) + "Load the host configuration file for the system `host-name'." + (interactive) + (let ((host-file (concat dotfiles/home "/hosts/" host-name ".org"))) + (when (file-exists-p host-file) + (org-babel-load-file host-file)))) + +;; Load a module definition. +(defun dotfiles/load-modules (modules) + "Load the `modules' in sequential order." + (interactive) + (dolist (m modules) + (let ((mod-file (concat dotfiles/home "/modules/" (symbol-name m) ".org"))) + (when (file-exists-p mod-file) + (org-babel-load-file mod-file))))) +#+end_src + * Config :PROPERTIES: :header-args: :tangle init.el @@ -48,6 +188,8 @@ Once the early-init phase as completed, there are only two remaining tasks to co ;; Please make any modifications there. #+end_src +** Hosts + The first task involves loading a machine-specific (host) configuration file. This gives the oppertunity for a host to intervene in the module loading process, adding or removing modules before the next stage has begun. This is accomplished by modifying the list of modules in ~dotfiles/modules~. #+begin_src emacs-lisp @@ -55,6 +197,8 @@ The first task involves loading a machine-specific (host) configuration file. Th (dotfiles/load-host system-name) #+end_src +** Modules + After the host configuration file has loaded, the value of ~dotfiles/modules~ is used to load all of the enabled modules. They're loaded in sequential order, and an error in any module will end this process. #+begin_src emacs-lisp @@ -69,3 +213,7 @@ After the host configuration file has loaded, the value of ~dotfiles/modules~ is [fn:2] https://gnu.org/distros/free-distros.html [fn:3] https://chrishayward.xyz/notes/literate-programming/ + +[fn:4] https://chrishayward.xyz/posts/immutable-emacs/ + +[fn:5] https://github.com/raxod502/straight.el diff --git a/early-init.el b/early-init.el index 8bca58d..147e04d 100644 --- a/early-init.el +++ b/early-init.el @@ -1,5 +1,114 @@ -(load-file "~/.emacs.d/elisp/cleanup.el") ;; Separate the immutable from the mutable. -(load-file "~/.emacs.d/elisp/packages.el") ;; Get the packages we want, how we want them. -(load-file "~/.emacs.d/elisp/options.el") ;; Load option declarations for host configurations. -(load-file "~/.emacs.d/elisp/hosts.el") ;; Load your hosts configurations. -(load-file "~/.emacs.d/elisp/modules.el") ;; Load your modules definitions. +;; This file is controlled by README.org +;; Please make any modifications there. + +;; The original value of `user-emacs-directory' prior to redirection. +(defconst dotfiles/home + (or (getenv "DOTFILES_HOME") + (expand-file-name user-emacs-directory))) + +;; The redirection target of `user-emacs-directory' during initialization. +(defconst dotfiles/cache + (or (getenv "DOTFILES_CACHE") + (expand-file-name "~/.cache/emacs"))) + +;; Make sure `dotfiles/cache' is a valid directory. +(unless (file-exists-p dotfiles/cache) + (make-directory dotfiles/cache t)) + +;; Redirect the value of `user-emacs-directory'. +(setq user-emacs-directory dotfiles/cache) + +;; Disable error messages for packages that don't support native-comp. +(setq comp-async-report-warnings-errors nil) + +;; Disable unwanted features. +(setq make-backup-files nil + create-lockfiles nil) + +;; Apply the configurations prior to bootstrapping the package manager. +(setq straight-repository-branch "master" + straight-use-package-by-default t + package-enable-at-startup nil) + +;; Bootstrap the package manager. +(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)) + +;; Integrate with `use-package' by installing it via `straight'. +(straight-use-package 'use-package) + +;; Specify core package sources. +(straight-use-package 'no-littering) +(straight-use-package '(org :local-repo nil) + +;; All of the modules available sorted in their default load order. +(defconst dotfiles/modules-p + '(org trash keys evil dired magit + shell mu4e elfeed eshell vterm + gpg pass x11 exwm roam agenda + spelling grammar reveal hugo + capture projects docker lsp dap + cc go uml conf python fonts ivy + themes modeline dashboard)) + +;; All of the enabled modules. +(defvar dotfiles/modules dotfiles/modules-p) + +;; The default system language. +(defvar dotfiles/language (getenv "LANG")) + +;; Configure a unified system font. +(defvar dotfiles/font "Fira Code") + +;; Default system font size. +(defvar dotfiles/font-size 96) + +;; Delay time before offering suggestions and completions. +(defvar dotfiles/idle 0.0) + +;; The all powerful leader key. +(defvar dotfiles/leader-key "SPC") + +;; Global prefix for the leader key under X11 windows. +(defvar dotfiles/leader-key-global + (concat "C-" dotfiles/leader-key)) + +;; The location on disk of source code projects. +(defvar dotfiles/projects + (or (getenv "DOTFILES_PROJECTS") + (expand-file-name "~/.local/source"))) + +;; The location on disk of the local copy of the password store. +(defvar dotfiles/passwords + (or (getenv "DOTFILES_PASSWORDS") + (expand-file-name "~/.password-store"))) + +;; The public GPG key to encrpyt files, and emails for / to / with. +(defvar dotfiles/public-key "37AB1CB72B741E478CA026D43025DCBD46F81C0F" + +;; Load a host configuration. +(defun dotfiles/load-host (host-name) + "Load the host configuration file for the system `host-name'." + (interactive) + (let ((host-file (concat dotfiles/home "/hosts/" host-name ".org"))) + (when (file-exists-p host-file) + (org-babel-load-file host-file)))) + +;; Load a module definition. +(defun dotfiles/load-modules (modules) + "Load the `modules' in sequential order." + (interactive) + (dolist (m modules) + (let ((mod-file (concat dotfiles/home "/modules/" (symbol-name m) ".org"))) + (when (file-exists-p mod-file) + (org-babel-load-file mod-file))))) diff --git a/elisp/.git-keep b/elisp/.git-keep deleted file mode 100644 index e69de29..0000000 diff --git a/elisp/cleanup.el b/elisp/cleanup.el deleted file mode 100644 index a02807d..0000000 --- a/elisp/cleanup.el +++ /dev/null @@ -1,34 +0,0 @@ -;; Cleanup - -;; Emacs creates a lot of files relative to `user-emacs-directory'. -;; These files are not part of this immutable configuration and do not belong in the emacs directory. - -(defconst dotfiles/home - (or (getenv "DOTFILES_HOME") - (expand-file-name user-emacs-directory))) - -(defconst dotfiles/cache - (or (getenv "DOTFILES_CACHE") - (expand-file-name "~/.cache/emacs"))) - -;; How can we solve this issue? - -(unless (file-exists-p dotfiles/cache) - (make-directory dotfiles/cache t)) - -;; Shortly after initialization, before most packages load, we change the value to `dotfiles/cache'. -;; I elaborate more on the technique in my post https://chrishayward.xyz/immutable-emacs/. - -(setq user-emacs-directory dotfiles/cache) - -;; Disable error messages for packages that do not yet support native compilation. - -(setq comp-async-report-warnings-errors nil) - -;; Because this project uses version-control, we can disable more unwanted features: - -;; + Lock files -;; + Backup files - -(setq make-backup-files nil - create-lockfiles nil) diff --git a/elisp/hosts.el b/elisp/hosts.el deleted file mode 100644 index 5bf1b03..0000000 --- a/elisp/hosts.el +++ /dev/null @@ -1,10 +0,0 @@ -;; Hosts - -;; Each host machines configuration loaded immediately after declaring the options, before applying any configuration. This allows system to system control while remaining immutable. Override any of the available options configurations in a host file. Begin the process by loading any host specific option overrides. The host configuration tangles, and loads (if it exist) using the systems name. If a host definition doesn't exist the default values remain. - -(defun dotfiles/load-host (host-name) - "Load the host configuration file for the system `host-name'." - (interactive) - (let ((host-file (concat dotfiles/home "/hosts/" host-name ".org"))) - (when (file-exists-p host-file) - (org-babel-load-file host-file)))) diff --git a/elisp/modules.el b/elisp/modules.el deleted file mode 100644 index d94353f..0000000 --- a/elisp/modules.el +++ /dev/null @@ -1,12 +0,0 @@ -;; Modules - -;; Breaking down the project into logical units or chapters to keep the code more maintainable and organized. This is also a fundamental requirement to achieve the goal of modularity. All of the modules in ~dotfiles/modules~ load after the host overrides. By default, all of the packages defined in ~dotfiles/modules-p~ load. Override this behaviour in a host configuration file. - - -(defun dotfiles/load-modules (modules) - "Load the `modules' in sequential order." - (interactive) - (dolist (m modules) - (let ((mod-file (concat dotfiles/home "/modules/" (symbol-name m) ".org"))) - (when (file-exists-p mod-file) - (org-babel-load-file mod-file))))) diff --git a/elisp/options.el b/elisp/options.el deleted file mode 100644 index 400c8d3..0000000 --- a/elisp/options.el +++ /dev/null @@ -1,44 +0,0 @@ -(defconst dotfiles/modules-p - '(org trash keys evil dired magit - shell mu4e elfeed eshell vterm - gpg pass x11 exwm roam agenda - spelling grammar reveal hugo - capture projects docker lsp dap - cc go uml conf python fonts ivy - themes modeline dashboard) - "All of the available modules.") - -(defvar dotfiles/modules dotfiles/modules-p - "All of the enable modules, default value equal to `dotfiles/modules-p'.") - -(defvar dotfiles/language (getenv "LANG") - "Default system dictionary language.") - -(defvar dotfiles/font "Fira Code" - "Unified system font family.") - -(defvar dotfiles/font-size 96 - "Unified system font size.") - -(defvar dotfiles/idle 0.0 - "Delay time before offering suggestions and completions.") - -(defvar dotfiles/leader-key "SPC" - "The all-powerful leader key.") - -(defvar dotfiles/leader-key-global - (concat "C-" dotfiles/leader-key) - "Global prefix for the all-powerful leader key.") - -(defvar dotfiles/projects - (or (getenv "DOTFILES_PROJECTS") - (expand-file-name "~/.local/source")) - "Location of source code projects.") - -(defvar dotfiles/passwords - (or (getenv "DOTFILES_PASSWORDS") - (expand-file-name "~/.password-store")) - "Location of the local password store.") - -(defvar dotfiles/public-key "37AB1CB72B741E478CA026D43025DCBD46F81C0F" - "GPG kley to encrpy org files for/to.") diff --git a/elisp/packages.el b/elisp/packages.el deleted file mode 100644 index 1913a51..0000000 --- a/elisp/packages.el +++ /dev/null @@ -1,35 +0,0 @@ -;; Download and instll packages using https://github.com/raxod502/straight.el -;; It's a functional package manager that integrates with https://github.com/jwiegley/use-package - -;; + Use the development branch -;; + Integrate with use-package - -;; Apply the configurations prior to bootstrapping the package manager. - -(setq straight-repository-branch "master" - straight-use-package-by-default t - package-enable-at-startup nil) - -;; Bootstrap the package manager. -;; Download, Install, or Configuring depending on the state of the configuration. -;; All packages build from source, pinned to specific git commit hashes. - -(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)) - -;; Integrate with use-package by installing it with straight. Override some package sources to -;; avoid the default package shipped with Emacs. - -(straight-use-package 'use-package) -(straight-use-package 'no-littering) -(straight-use-package '(org :local-repo nil))