;;; early-init.el --- -*- lexical-binding: t; coding: utf-8; no-byte-compile: t; -*- ;;; Commentary: ;; DEBUG FLAGS ;; Debug flags are default on while this config is loading since errors should ;; be loud and highly noticeable. They are restored to their default off once ;; the config has completed. ;; STARTUP PERFORMANCE ;; Increasing garbage collection to a very high number decreases startup time. ;; setting the file-name-handler and vc-handled-backends avoids some regexp ;; slowness during startup. All original values are restored once Emacs is ;; finished with startup. ;; LOCAL REPOSITORIES ;; This config doesn't work if the packages it relies on fail. Having local ;; package repositories also allows for full config portability behind corporate ;; firewalls and fast recovery from package issues no matter the network ;; situation. ;; The localrepo directory contains all the last known good packages for this ;; config. The directory is added as a repository to the package archive list ;; first, and given the highest priority number. This allows for a portable ;; installation and reinstallation. This directory averages ~70 MB. ;; Having a full local mirror of all elpa, melpa, and org repositories gives you ;; more flexibility but at a higher storage cost. The script ;; 'create-elpa-mirror.sh in user-emacs-directory/scripts directory will clone ;; them all locally. As of Saturday, March 30, 2024 the directory containing all ;; gnu, nongnu, melpa, melpa-stable, and org packages takes around 1.9 GB. ;; For more information on the localrepo and elpa mirrors, read the commentary ;; in local-repository.el. ;;; Code: ;; ---------------------------- Benchmark Init Setup --------------------------- ;; Comprehensive startup profiling (run M-x benchmark-init/show-durations-tree) ;; To disable profiling, comment out the lines below. ;; Note: Install with M-x package-install RET benchmark-init RET ;; (when (require 'benchmark-init nil 'noerror) ;; (add-hook 'after-init-hook 'benchmark-init/deactivate)) ;; -------------------------------- Debug Flags -------------------------------- ;; debugging enabled during Emacs startup. disabled again after Emacs startup. ;; uncomment when repo signatures expire and package installation is necessary ;; (setq package-check-signature nil) (setq debug-on-error t) ;; default nil. turn on to debug issues only. (setq debug-on-quit t) ;; debug on C-g (breaking out of hangs/freezes) (add-hook 'emacs-startup-hook (lambda () (setq debug-on-error nil) (setq debug-on-quit nil))) ;; ------------------------------ Bug Workarounds ------------------------------ ;; Prevent org-element from being natively compiled again by adding the line (setq native-comp-jit-compilation-deny-list '(".*org-element.*")) ;; --------------------------- Warning Notifications --------------------------- ;; skip warnings but notify me about errors (setq warning-minimum-level :error) ;; --------------------------- Use Online Repos Flag --------------------------- ;; set to nil to only use localrepo and local elpa-mirrors (see script directory) (defvar cj/use-online-repos t "Whether to check for network connectivity & use online package repositories.") ;; Cache network status to avoid repeated checks (defvar cj/network-available :unknown "Cached network availability status. Can be t (available), nil (not available), or :unknown (not checked yet).") ;; ---------------------------- Startup Performance ---------------------------- ;; increases garbage collection threshold, and turns off file-name-handler and ;; vc-backends during startup and restores the settings once emacs has loaded. (defvar cj/orig-gc-cons-threshold gc-cons-threshold "Temporary variable to allow restoration of value post-startup.") (setq gc-cons-threshold most-positive-fixnum) (defvar cj/orig-file-name-handler-alist file-name-handler-alist "Temporary variable to allow restoration of value post-startup.") (setq file-name-handler-alist nil) (defvar cj/orig-vc-handled-backends vc-handled-backends "Temporary variable to allow restoration of value post-startup.") (setq vc-handled-backends nil) (add-hook 'emacs-startup-hook (lambda () (setq gc-cons-threshold cj/orig-gc-cons-threshold file-name-handler-alist cj/orig-file-name-handler-alist vc-handled-backends cj/orig-vc-handled-backends))) ;; ------------------------------ Site Start Files ----------------------------- ;; don't load site-start or default.el files (setq inhibit-default-init t) ;; ------------------------------- Network Check ------------------------------- ;; checks if the network is available. used for online repo enablement. (defvar cj/network-available :unknown "Cached network availability status. Values: :unknown, t, or nil.") (defun cj/reset-network-cache () "Reset cached network availability." (setq cj/network-available :unknown)) (defun cj/internet-up-p (&optional force) "Return non-nil if the network seems available. If FORCE is non-nil, re-check even if cached." (when (or force (eq cj/network-available :unknown) (not (boundp 'cj/network-available))) (setq cj/network-available (condition-case _err (pcase system-type ;; Linux, *BSD ((or 'gnu/linux 'berkeley-unix 'gnu) (= 0 (call-process "ping" nil nil nil "-c" "1" ; count: 1 packet "-W" "1" ; timeout: 1 second "1.1.1.1"))) ;; macOS ('darwin (= 0 (call-process "ping" nil nil nil "-c" "1" "-t" "1" "1.1.1.1"))) ;; Windows ('windows-nt (= 0 (call-process "ping" nil nil nil "-n" "1" "-w" "1000" "1.1.1.1"))) ;; Fallback (_ (with-timeout (1.0 nil) (let ((p (ignore-errors (open-network-stream "net-test" nil "1.1.1.1" 53 :type 'datagram)))) (prog1 (and p t) (when p (delete-process p))))))) (error nil)))) cj/network-available) (setq package-enable-at-startup nil) (require 'package) ;; emacs built-in (defconst user-home-dir (getenv "HOME") "The user's home directory per the environment variable.") (defconst elpa-mirror-location (concat user-home-dir ".elpa-mirrors/") "The path to the elpa mirror location.") (defconst localrepo-location (concat user-emacs-directory ".localrepo/") "The path to your local Emacs package repository. For more information about the local Emacs package repository, see comments in early-init.el.") (setq package-archives nil) ;; package-archives will be added below ;; LOCAL REPOSITORY (packages in version control) (when (file-accessible-directory-p localrepo-location) (add-to-list 'package-archives (cons "localrepo" localrepo-location) t) (add-to-list 'package-archive-priorities '("localrepo" . 200))) ;; LOCAL REPOSITORY ELPA MIRRORS (when (file-accessible-directory-p (concat elpa-mirror-location "gnu")) (add-to-list 'package-archives (cons "gnu-local" (concat elpa-mirror-location "gnu/")) t) (add-to-list 'package-archive-priorities '("gnu-local" . 125))) (when (file-accessible-directory-p (concat elpa-mirror-location "nongnu")) (add-to-list 'package-archives (cons "nongnu-local" (concat elpa-mirror-location "nongnu/")) t) (add-to-list 'package-archive-priorities '("nongnu-local" . 120))) (when (file-accessible-directory-p (concat elpa-mirror-location "melpa")) (add-to-list 'package-archives (cons "melpa-local" (concat elpa-mirror-location "melpa/")) t) (add-to-list 'package-archive-priorities '("melpa-local" . 115))) (when (file-accessible-directory-p (concat elpa-mirror-location "stable-melpa")) (add-to-list 'package-archives (cons "melpa-stable-local" (concat elpa-mirror-location "stable-melpa/")) t) (add-to-list 'package-archive-priorities '("melpa-stable-local" . 100))) ;; ONLINE REPOSITORIES (when (and cj/use-online-repos (cj/internet-up-p)) (add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/") t) (add-to-list 'package-archive-priorities '("gnu" . 25)) (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/") t) (add-to-list 'package-archive-priorities '("nongnu" . 20)) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (add-to-list 'package-archive-priorities '("melpa" . 15)) (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t) (add-to-list 'package-archive-priorities '("melpa-stable" . 5))) ;; Initialize package system (package-initialize) ;; Package refresh logic - refresh only if: ;; 1. Online repos are enabled AND network is available ;; 2. Any online repo cache doesn't exist or is older than 7 days (when (and cj/use-online-repos (cj/internet-up-p)) (let ((cache-age-days 7) (needs-refresh nil)) ;; Check each online repository's cache (dolist (archive '("gnu" "nongnu" "melpa" "melpa-stable")) (let ((cache-file (expand-file-name (format "archives/%s/archive-contents" archive) package-user-dir))) (when (or (not (file-exists-p cache-file)) (> (/ (float-time (time-subtract (current-time) (file-attribute-modification-time (file-attributes cache-file)))) 86400.0) ;; == 7 days cache-age-days)) (setq needs-refresh t)))) ;; Only refresh if needed (when needs-refresh (condition-case nil (package-refresh-contents) (error (message "Failed to refresh package contents")))))) ;; Ensure use-package is installed (unless (package-installed-p 'use-package) (unless package-archive-contents (package-refresh-contents)) (package-install 'use-package)) ;;(require 'use-package-ensure) ; Needed for :ensure to work (setq use-package-always-ensure t) ; Auto-install packages ;; Package signature checking (setq package-check-signature nil) ;; (setq package-check-signature t) ;; Optional but recommended for better error messages during config loading ;;(setq use-package-expand-minimally nil) ; Better error reporting ;;(setq use-package-compute-statistics t) ; Set to t if you want timing info ;; turn on for use-package debugging ;;(setq use-package-verbose nil) ;; ---------------------------------- Unicode ---------------------------------- ;; unicode all the things (set-locale-environment "en_US.UTF-8") (prefer-coding-system 'utf-8) (set-default-coding-systems 'utf-8) (set-terminal-coding-system 'utf-8) (set-keyboard-coding-system 'utf-8) (set-selection-coding-system 'utf-8) (setq locale-coding-system 'utf-8) (set-charset-priority 'unicode) (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) ;; ---------------------------- Inhibit UI Elements ---------------------------- ;; setting UI preferences here before the UI is displayed (push '(menu-bar-lines . 0) default-frame-alist) (push '(tool-bar-lines . 0) default-frame-alist) (push '(vertical-scroll-bars . nil) default-frame-alist) (setq-default inhibit-startup-screen t) (setq-default inhibit-startup-message t) (setq-default inhibit-splash-screen t) (setq-default initial-scratch-message nil) (setq inhibit-startup-echo-area-message (user-login-name)) ;; Disable bidirectional text rendering for slight performance boost (setq-default bidi-display-reordering nil ;; disable bidi reordering for speed bidi-paragraph-direction 'left-to-right bidi-inhibit-bpa t) ;; additional speedup ;; Disable global font lock mode until after initialization (setq-default global-font-lock-mode nil) (add-hook 'emacs-startup-hook #'global-font-lock-mode) (provide 'early-init) ;;; early-init.el ends here