diff --git a/dotfiles/emacs.d/elpaca-installer.el b/dotfiles/emacs.d/elpaca-installer.el index c50c5052..8e7bc681 100644 --- a/dotfiles/emacs.d/elpaca-installer.el +++ b/dotfiles/emacs.d/elpaca-installer.el @@ -8,6 +8,75 @@ :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca-activate))) + +(defun elpaca-installer--repo-installer-version (repo) + "Return the installer version expected by elpaca checkout at REPO." + (let ((elpaca-el (expand-file-name "elpaca.el" repo))) + (when (file-exists-p elpaca-el) + (with-temp-buffer + (insert-file-contents elpaca-el) + (when (re-search-forward + "(= elpaca-installer-version \\([0-9.]+\\))" + nil t) + (match-string 1)))))) + +(defun elpaca-installer--build-source-root (build) + "Infer the source directory backing BUILD from its symlinked files." + (let ((roots (delete-dups + (delq nil + (mapcar + (lambda (dir) + (when (file-exists-p dir) + (file-name-as-directory (file-truename dir)))) + (list elpaca-sources-directory + elpaca-legacy-repos-directory)))))) + (catch 'repo + (dolist (entry (directory-files build t directory-files-no-dot-files-regexp)) + (when-let* ((target (file-symlink-p entry)) + (truename (ignore-errors (file-truename entry))) + (source-root (and truename + (directory-file-name + (file-name-directory truename))))) + (dolist (root roots) + (when (string-prefix-p root truename) + (when (file-directory-p source-root) + (throw 'repo source-root)))))) + nil))) + +(defun elpaca-installer--repair-build-source-layout () + "Repair or prune builds whose expected source directory is missing." + (when (and (file-directory-p elpaca-builds-directory) + (file-directory-p elpaca-sources-directory)) + (dolist (build (directory-files elpaca-builds-directory t directory-files-no-dot-files-regexp)) + (when (file-directory-p build) + (let* ((name (file-name-nondirectory (directory-file-name build))) + (source (expand-file-name name elpaca-sources-directory)) + (desired-source (when-let ((root (elpaca-installer--build-source-root build))) + (directory-file-name root))) + (current-source (when (or (file-exists-p source) + (file-symlink-p source)) + (ignore-errors + (directory-file-name (file-truename source)))))) + (cond + ((and desired-source + current-source + (equal current-source desired-source)) + nil) + ((and (file-directory-p source) + (not (file-symlink-p source))) + nil) + ((file-symlink-p source) + (delete-file source) + (if desired-source + (make-symbolic-link desired-source + (directory-file-name source)) + (delete-directory build 'recursive))) + ((file-exists-p source) nil) + (desired-source + (make-symbolic-link desired-source + (directory-file-name source))) + (t + (delete-directory build 'recursive)))))))) ;; Elpaca now expects package sources under `sources/`. Preserve older local ;; installs that still use `repos/` so startup can recover without recloning. (when (and (file-directory-p elpaca-legacy-repos-directory) @@ -18,11 +87,22 @@ (not (file-exists-p elpaca-legacy-repos-directory))) (make-symbolic-link (directory-file-name elpaca-sources-directory) (directory-file-name elpaca-legacy-repos-directory))) +(elpaca-installer--repair-build-source-layout) (let* ((repo (expand-file-name "elpaca/" elpaca-sources-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (autoloads (expand-file-name "elpaca-autoloads" repo)) (order (cdr elpaca-order)) (default-directory repo)) + ;; Older elpaca checkouts can no longer bootstrap the current installer. + ;; Reset only elpaca's own repo/build so startup can self-heal. + (when-let ((repo-version (elpaca-installer--repo-installer-version repo)) + ((not (equal repo-version (format "%s" elpaca-installer-version))))) + (when (file-directory-p build) + (delete-directory build 'recursive)) + (when (file-directory-p elpaca-cache-directory) + (delete-directory elpaca-cache-directory 'recursive)) + (when (file-directory-p repo) + (delete-directory repo 'recursive))) (add-to-list 'load-path repo) (when (file-exists-p build) (add-to-list 'load-path build)) diff --git a/dotfiles/emacs.d/init.el b/dotfiles/emacs.d/init.el index c8893be4..b6dd8e4e 100644 --- a/dotfiles/emacs.d/init.el +++ b/dotfiles/emacs.d/init.el @@ -14,6 +14,7 @@ ;; Default hosted git clones to SSH (e.g., git@github.com:owner/repo.git). (setq elpaca-order-defaults (plist-put elpaca-order-defaults :protocol 'ssh)) (elpaca elpaca-use-package (elpaca-use-package-mode)) +(elpaca-wait) (setq use-package-enable-imenu-support t) (setq use-package-always-ensure t) @@ -76,7 +77,7 @@ ;; Without this, org can behave very strangely (use-package org :ensure - (org :type git :host github :repo "colonelpanic8/org-mode" :local-repo "org" + (org :type git :host github :repo ("colonelpanic8/org-mode" . "org") :branch "my-main-2025" :depth full :files (:defaults "lisp/*.el" ("etc/styles/" "etc/styles/*")) @@ -95,6 +96,22 @@ :demand t) (elpaca-wait) +;; Magit's split packages are compiled separately; make them available before +;; the larger config queue reaches magit itself. +(use-package git-commit + :ensure (:host github :repo "magit/magit" + :files ("lisp/git-commit.el" "lisp/git-commit-pkg.el") + :wait t) + :defer t) + +(use-package magit-section + :ensure (:host github :repo "magit/magit" + :files ("lisp/magit-section.el" "lisp/magit-section-pkg.el") + :wait t) + :defer t) + +(elpaca-wait) + (when (or (equal (s-trim (shell-command-to-string "whoami")) "kat") imalison:kat-mode) (let ((debug-on-error t)) diff --git a/dotfiles/emacs.d/org-config.org b/dotfiles/emacs.d/org-config.org index 0921bca6..d3d9f562 100644 --- a/dotfiles/emacs.d/org-config.org +++ b/dotfiles/emacs.d/org-config.org @@ -1095,10 +1095,27 @@ alphanumeric characters only." (goto-char (marker-position marker)) ,(macroexpand '(imalison:assigned-to-me)))))) (org-wild-notifier-predicate-blacklist - '(org-wild-notifier-done-keywords-predicate)) + '(org-wild-notifier-done-keywords-predicate)) (org-wild-notifier-show-any-overdue-with-day-wide-alerts t) (org-wild-notifier-day-wide-alert-times '("10pm"))) :config + (defun org-wild-notifier--retrieve-events () + "Get events from agenda view without reinitializing package.el." + `(lambda () + (setf org-agenda-use-time-grid nil) + (setf org-agenda-compact-blocks t) + ,(async-inject-variables (org-wild-notifier-environment-regex)) + (require 'org) + (require 'org-wild-notifier) + (org-agenda-list 2 (org-read-date nil nil "today")) + (->> (org-split-string (buffer-string) "\n") + (--map (plist-get + (org-fix-agenda-info (text-properties-at 0 it)) + 'org-marker)) + (-non-nil) + (org-wild-notifier--apply-whitelist) + (org-wild-notifier--apply-blacklist) + (-map 'org-wild-notifier--gather-info)))) (org-wild-notifier-mode +1) (defun org-wild-notify-check-at-time () (interactive)