dots/chemacs/.emacs.d/chemacs.el

171 lines
7.3 KiB
EmacsLisp

;;; chemacs.el --- -*- lexical-binding: t; -*-
;;; Commentary:
;; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;;
;; ___ ___ ___ ___ ___ ___ ___
;; / /\ /__/\ / /\ /__/\ / /\ / /\ / /\
;; / /:/ \ \:\ / /:/_ | |::\ / /::\ / /:/ / /:/_
;; / /:/ \__\:\ / /:/ /\ | |:|:\ / /:/\:\ / /:/ / /:/ /\
;; / /:/ ___ ___ / /::\ / /:/ /:/_ __|__|:|\:\ / /:/~/::\ / /:/ ___ / /:/ /::\
;; /__/:/ / /\ /__/\ /:/\:\ /__/:/ /:/ /\ /__/::::| \:\ /__/:/ /:/\:\ /__/:/ / /\ /__/:/ /:/\:\
;; \ \:\ / /:/ \ \:\/:/__\/ \ \:\/:/ /:/ \ \:\~~\__\/ \ \:\/:/__\/ \ \:\ / /:/ \ \:\/:/~/:/
;; \ \:\ /:/ \ \::/ \ \::/ /:/ \ \:\ \ \::/ \ \:\ /:/ \ \2.0 /:/
;; \ \:\/:/ \ \:\ \ \:\/:/ \ \:\ \ \:\ \ \:\/:/ \__\/ /:/
;; \ \::/ \ \:\ \ \::/ \ \:\ \ \:\ \ \::/ /__/:/
;; \__\/ \__\/ \__\/ \__\/ \__\/ \__\/ \__\/
;;
;; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;;
;; Chemacs - Emacs Profile Switcher
;;
;; See README.md for instructions.
;; NOTE Don't require any libraries in this file. When emacs loads a library that
;; is byte compiled, it may start native-compiling it, so if we require anything
;; here, native compilation can start before the user has had a chance to configure
;; it in their init files.
;;; Code:
(defvar chemacs-version "2.0")
(defvar config-home (or (getenv "XDG_CONFIG_HOME") "~/.config"))
(defvar chemacs-profiles-paths (list "~/.emacs-profiles.el" (format "%s/%s" config-home "chemacs/profiles.el" )))
(defvar chemacs-default-profile-paths (list "~/.emacs-profile" (format "%s/%s" config-home "chemacs/profile")))
;; Copy `seq' library's `seq-filter' to avoid requiring it, see note above.
(defun chemacs--seq-filter (pred sequence)
(let ((exclude (make-symbol "exclude")))
(delq exclude (mapcar (lambda (elt)
(if (funcall pred elt)
elt
exclude))
sequence))))
(defvar chemacs-profiles-path (or (car (chemacs--seq-filter #'file-exists-p chemacs-profiles-paths))
(car chemacs-profiles-paths)))
(defvar chemacs-default-profile-path (or (car (chemacs--seq-filter #'file-exists-p chemacs-default-profile-paths))
(car chemacs-default-profile-paths)))
(defun chemacs-handle-command-line (args)
(when args
;; Handle either --with-profile profilename or
;; --with-profile=profilename
(let ((s (split-string (car args) "=")))
(cond ((equal (car args) "--with-profile")
;; This is just a no-op so Emacs knows --with-profile
;; is a valid option. If we wait for
;; command-switch-alist to be processed then
;; after-init-hook has already run.
(add-to-list 'command-switch-alist
'("--with-profile" .
(lambda (_) (pop command-line-args-left))))
(cadr args))
;; Similar handling for `--with-profile=profilename'
((equal (car s) "--with-profile")
(add-to-list 'command-switch-alist `(,(car args) . (lambda (_))))
(mapconcat 'identity (cdr s) "="))
(t (chemacs-handle-command-line (cdr args)))))))
(defvar chemacs--with-profile-value
(let* ((value (chemacs-handle-command-line command-line-args))
(read-value (read value)))
(when value
(if (listp read-value)
read-value
value))))
(defvar chemacs-literal-profile-provided
(and chemacs--with-profile-value
(listp chemacs--with-profile-value)))
(unless (or (file-exists-p chemacs-profiles-path)
(and chemacs--with-profile-value
(listp chemacs--with-profile-value)))
(error "[chemacs] %s does not exist." chemacs-profiles-path))
(defvar chemacs-default-profile-name
(if (file-exists-p chemacs-default-profile-path)
(with-temp-buffer
(insert-file-contents chemacs-default-profile-path)
(goto-char (point-min))
;; (buffer-string))
(symbol-name (read (current-buffer)) ))
"default"))
(defvar chemacs-profile-name
(if (and chemacs--with-profile-value
(stringp chemacs--with-profile-value))
chemacs--with-profile-value
chemacs-default-profile-name))
(defvar chemacs-profile
(if (and chemacs--with-profile-value
(listp chemacs--with-profile-value))
chemacs--with-profile-value
(let ((profiles
(with-temp-buffer
(insert-file-contents chemacs-profiles-path)
(goto-char (point-min))
(read (current-buffer)))))
(cdr (assoc chemacs-profile-name profiles)))))
(unless chemacs-profile
(error "No profile `%s' in %s" chemacs-profile-name chemacs-profiles-path))
(defun chemacs-profile-get (key &optional default)
(alist-get key chemacs-profile default))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(setq user-emacs-directory (file-name-as-directory
(chemacs-profile-get 'user-emacs-directory)))
;; Allow multiple profiles to each run their server
;; use `emacsclient -s profile_name' to connect
(let ((name (chemacs-profile-get 'server-name)))
(when name (setq server-name name)))
;; Set environment variables, these are visible to init-file with
;; getenv
(mapcar (lambda (env)
(setenv (car env) (cdr env)))
(chemacs-profile-get 'env))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun chemacs-load-user-early-init ()
(let ((early-init-file (expand-file-name "early-init.el" user-emacs-directory)))
(load early-init-file t t)))
(defun chemacs-load-user-init ()
(when (chemacs-profile-get 'straight-p) (chemacs-load-straight))
(let ((init-file (expand-file-name "init.el" user-emacs-directory)))
(setq package-user-dir (expand-file-name "elpa" user-emacs-directory))
(load init-file t t)
;; Prevent customize from changing ~/.emacs (this file), but if
;; init.el has set a value for custom-file then don't touch it.
(let ((chemacs-custom-file (chemacs-profile-get 'custom-file init-file)))
(when (not custom-file)
(setq custom-file chemacs-custom-file)
(unless (equal custom-file init-file)
(unless (file-exists-p custom-file)
(with-temp-buffer (write-file custom-file)))
(load custom-file))))))
(defun chemacs-load-straight ()
(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)))
(provide 'chemacs)