#+ TITLE minimal emacs startup
Emacs 27 introduces early-init.el to go stuff before the graphics - basically package and some gui suppression
These are speedup and safety changes but note that Aquamacs has probably already taken most of the time.Elisps and init files have two ways of doing this. One is to put the whole init inside a let block but I want each src block to be runnable from the org file so not good. The other is to set and copy old values at the beginning then use an end hook to put them back. A long term alternative is to make the loader function do that work.
The time is probably not that useful - build on Aquamacs instead and there is an emacs profiler. (the profiler for startup files only really work if there is one init file) I did get the Aquamacs nightly and that is much slower and displaying menus is an issue. I have changed accessibility in System Preferences->Privacy so we will see. Probably wait until emacs 27 is out and use a plain emacs and see about spell checking
The file-name-handler-alist is probably needed as some of the hooks slow things heavily
This is from John Wiegley Also from doom via https://github.com/xenodium/dotsies/blob/main/emacs/early-init.el
;;(defconst emacs-start-time (current-time))
(defvar file-name-handler-alist-old file-name-handler-alist)
(setq file-name-handler-alist nil
message-log-max 16384
gc-cons-threshold 402653184
gc-cons-percentage 0.6
auto-window-vscroll nil)
We need to sort out paths - ideally after init timers etc but we also need them to load early-init so timer is less accurate but then it is wrong for aquamacs anyway
In constant mwb-user-emacs-directory;; Need the directory from here.
(defconst mwb-user-emacs-directory
(file-name-directory (or load-file-name buffer-file-name)))
(defvar mwb-emacs-work-dir (expand-file-name "~/.local/emacs" )
"Directory on local machine wwhere emacs outside start directory.")
(defvar mwb-dropbox-root
(expand-file-name "~/Library/CloudStorage/Dropbox")
"Where dropbox is")
(defvar mwb-icloud-root
(expand-file-name "~/Library/Mobile Documents/com~apple~CloudDocs")
"Where icloud is")
(defvar mwb-emacs-share-dir
(concat mwb-dropbox-root "/data/emacs")
"Directory on local machine wwhere emacs outside start directory.")
(defvar mwb-emacs-eln-cache-dir
(expand-file-name "eln-cache" mwb-emacs-work-dir))
(make-directory mwb-emacs-eln-cache-dir t)
(when (>= emacs-major-version 28)
(if (fboundp 'startup-redirect-eln-cache)
(startup-redirect-eln-cache mwb-emacs-eln-cache-dir)
(add-to-list 'native-comp-eln-load-path mwb-emacs-eln-cache-dir)))
Ignore warnings as we can’t do much about them as most code is not written by me.
(setq native-comp-async-report-warnings-errors 'silent)
(defun mwb-user-emacs-file (name)
"Return an absolute per-user Emacs-specific file name around where the init file is.
It is basically locate-user-emacs-file but I have followed Aquamacs is setting that not where my init.el file is.
Main reason to use is so that I can put init under version control and the rest go elsewhere."
(expand-file-name name mwb-user-emacs-directory))
(setq package-user-dir
(expand-file-name
(format "elpa/%s" emacs-major-version) mwb-user-emacs-directory))
(setq package-quickstart-file
(expand-file-name
(format "package-quickstart.%d.el" emacs-major-version)
mwb-user-emacs-directory))
(setq package-enable-at-startup nil)
;;(menu-bar-mode -1)
(unless (and (display-graphic-p) (eq system-type 'darwin))
(push '(menu-bar-lines . 0) default-frame-alist))
(push '(tool-bar-lines . 0) default-frame-alist)
;;(push '(vertical-scroll-bars) default-frame-alist)
(setq tool-bar-mode nil)
(setq mode-line-format nil)
(setq orig-frame-inhibit-implied-resize frame-inhibit-implied-resize)
(setq frame-inhibit-implied-resize t)
(setq load-prefer-newer t)
(setq use-package-enable-imenu-support t)
(setq use-package-enable-imenu-support t)
All this is tangled into init.el which is also under git.
For Emacs < 27 we need early -init. All my fancy directory stuff is in early-init so we don’t know where to find this so hard code :((when (version< emacs-version "27")
(load (expand-file-name "~/.emacs.d/early-init")))
;; Added by Package.el. This must come before configurations of
;; installed packages. Don't delete this line. If you don't want it,
;; just comment it out by adding a semicolon to the start of the line.
;; You may delete these explanatory comments.
;(package-initialize)
This slows things down so for debugging outside init. but I now don’t like Backtrace mainly as trying to use emacs not edit it. However for debugging init we don’t want the backtrace
Set for init - can chang in init-mwb-hook-after-init. Later toggles in main hydra.;; setting to nil turns them off
(setq debug-on-error t)
;; (setq debug-on-error nil)
(setq debug-on-quit nil)
(setq init-file-debug 'nil)
;; (setq init-file-debug t)
(defvar modi/variables-to-be-watched ()
"List of variables to be watched.
Used by `modi/set-variable-watchers' and
`modi/unset-variable-watchers'")
(defun modi/variable-watcher-fn (symbol newval operation where)
"Print message when the value of variable SYMBOL changes.
The message shows the NEWVAL it changed to, the OPERATION that
caused that, and the buffer WHERE that happened if the value
change was buffer-local."
(message (format "[Watcher: %s] Now set to %S, by `%S'%s"
(symbol-name symbol)
newval
operation
(if where
(format " in %S" where)
""))))
(defun modi/set-variable-watchers ()
"Enable printing messages when any watched variable changes.
The variables to be watched should be added to
`modi/variables-to-be-watched'."
(interactive)
(dolist (var modi/variables-to-be-watched)
(add-variable-watcher var #'modi/variable-watcher-fn)))
(defun modi/unset-variable-watchers ()
"Disable variable watchers.
Variable watching will be disabled for the list of variables set
in `modi/variables-to-be-watched'."
(interactive)
(dolist (var modi/variables-to-be-watched)
(remove-variable-watcher var #'modi/variable-watcher-fn)))
(add-to-list 'modi/variables-to-be-watched 'debug-on-quit)
(modi/set-variable-watchers)
;; Trace some function
;; (require 'trace)
;; (untrace-all)
;; (trace-function-foreground 'iso-transl-define-keys)
;; (require 'profiler)
;; (profiler-start 'cpu+mem)
;; (add-hook-lambda after-init-hook (profiler-stop))
(defvar mwb-message-offset (float-time) "Time of last message")
(defun my-message-with-timestamp (old-func fmt-string &rest args)
"Prepend current timestamp (with microsecond precision) to a message"
(apply old-func
(concat (format-time-string "[%F %T] ")
fmt-string)
args))
(defun my-message-with-timediff (old-func fmt-string &rest args)
"Prepend difference in time to a message"
(let* ((now (float-time))
(diff (- now mwb-message-offset)))
(setq mwb-message-offset now)
(apply old-func
(if (> diff 0.1)
(concat (format "[%g] " diff)
fmt-string)
fmt-string)
args)))
(advice-add 'message :around #'my-message-with-timediff)
(defun mwb-message-remove-timediff ()
(interactive)
(advice-remove 'message #'my-message-with-timediff)
(message "remove timestamp"))
(defvar mwb-message-timestamp 'nil "true iff message should show timestamp")
(defun toggle-mwb-message-timestamp ()
(interactive)
(if mwb-message-timestamp
(progn
(advice-remove 'message #'my-message-with-timestamp)
(setq mwb-message-timestamp 'nil)
(message "remove timestamp"))
(advice-add 'message :around #'my-message-with-timestamp)
(setq mwb-message-timestamp t)
(message "add timestamp")))
(add-hook 'after-init-hook 'mwb-message-remove-timediff)
Function to load the code for this part of the init. Currently it just loads the .el of that name so could just be (load “mwb-init-load”). I now tangle all org-mode buffers on save. Eventually it will get the data from mwb-init-load.org and tangle it and use that.
Thse are required elisp for initialisation This is from nullman’s init files]] withn a rename to show it is not part of org.(defun nullman/org-babel-generate-elisp-file (file &optional byte-compile force)
"Generate an emacs-lisp file from an org-babel FILE.
Additionally, byte compile the file if BYTE-COMPILE is
non-nil.
Process file even if timestamp is not newer than target if FORCE
is non-nil."
(let* ((case-fold-search t)
(file-base (expand-file-name (file-name-sans-extension file)))
(file-org (concat file-base ".org"))
(file-elisp (concat file-base ".el"))
(file-comp (concat file-base ".elc"))
(heading-regexp "^\*+ ")
(heading-comment-regexp "^\*+ COMMENT ")
(begin-regexp "^[ \t]*#\\+BEGIN_SRC emacs-lisp")
(begin-tangle-regexp "^[ \t]*#\\+BEGIN_SRC .*:tangle ")
(end-regexp "^[ \t]*#\\+END_SRC")
(indent-regexp "^ "))
;; generate elisp file if needed
(when (or force
(not (file-exists-p file-elisp))
(file-newer-than-file-p file-org file-elisp))
(message "Nullman Writing %s..." file-elisp)
(with-temp-file file-elisp
(insert-file-contents file)
(goto-char (point-min))
(let (code
headings-counts
(level 1)
(comment-level 0)
(end-comment ""))
(while (not (eobp))
(cond
;; comment heading
((let ((case-fold-search nil))
(looking-at heading-comment-regexp))
(setq level (/ (- (match-end 0) (line-beginning-position) 8) 2))
(when (or (zerop comment-level)
(< level comment-level))
(setq comment-level level))
(delete-region (line-beginning-position) (progn (forward-line) (point))))
;; normal heading
((looking-at heading-regexp)
(setq level (/ (- (match-end 0) (line-beginning-position)) 2))
(when (or (zerop comment-level)
(<= level comment-level))
(setq comment-level 0)
(if (assoc level headings-counts)
(setf (cdr (assoc level headings-counts))
(cons (buffer-substring-no-properties (match-end 0) (line-end-position)) 1))
(setq headings-counts (append headings-counts (list (cons level (cons "No heading" 1)))))))
(delete-region (line-beginning-position) (progn (forward-line) (point))))
;; start of tangled source block
((and (looking-at begin-regexp)
(zerop comment-level)
(not (looking-at begin-tangle-regexp))) ; skip blocks with their own tangle directive
(let* ((heading-count (cdr (assoc level headings-counts)))
(heading (car heading-count))
(count (cdr heading-count)))
(delete-region (line-beginning-position) (progn (forward-line) (point)))
(unless (bobp)
(newline))
;; (when (fboundp 'org-link-escape)
;; (insert (format ";; [[file:%s::*%s][%s:%s]]\n" file-org (org-link-escape heading) heading count))
;; (setq end-comment (format ";; %s:%s ends here\n" heading count))
;; (cl-incf (cddr (assoc level headings-counts))))
(setq code t)))
;; end of tangled source block
((and code
(looking-at end-regexp))
(delete-region (line-beginning-position) (progn (forward-line) (point)))
(insert end-comment)
(setq code nil
end-comment ""))
;; inside tangled source block
(code
(when (looking-at indent-regexp)
(delete-char (if (boundp 'org-edit-src-content-indentation)
org-edit-src-content-indentation
2)))
(forward-line))
;; outside tangled source block
(t
(delete-region (line-beginning-position) (progn (forward-line) (point))))))
(time-stamp))
(message "Nullman Wrote %s..." file-elisp)))
))
(setq mwb-esup-depth 1) ; Some attempt at benchmarking
(defun mwb-init--load (file-root-abs &optional no-org)
"Load the relevant code.
<file-root> is an absolute file root
Look for <file-root>.org and <file-root>.el files.
If org and no el or org file is newer then retangle the org file if noorg is not nil then use nullmans expand then load <file-root>.el "
(let* ((org-file
(concat file-root-abs ".org"))
(el-file
(concat file-root-abs ".el")))
(setq esup-depth mwb-esup-depth)
;; (setq esup-child-max-depth mwb-esup-depth )
(setq esup-child-current-depth 0)
(when (file-newer-than-file-p org-file el-file)
;; (let ((org-att (file-attributes org-file) )
;; (el-time (file-attribute-access-time el-file))
;; (org-time (file-attribute-access-time org-att)))
;; (message "Generating .el from org for <%s> times %s %s"
;; file-root-abs
;; org-time
;; el-time
;; ))
(cond (no-org
(message "tangle <%s> to <%s> using regex replacement not org mode"
org-file el-file)
(nullman/org-babel-generate-elisp-file org-file el-file))
(t
(require 'org)
(message "This loaded an org mode but from the system - best to restart")
(message "tangle <%s> to <%s> using org version %s"
org-file el-file org-version)
(org-babel-tangle-file org-file el-file))))
(condition-case err
(load el-file)
(error (let ((msg (format-message "Error loading %s: \"%s\""
el-file
(error-message-string err))))
(warn msg)
(message msg))))))
(defun mwb-init-load (file-root &optional no-org)
(mwb-init--load (expand-file-name file-root mwb-user-emacs-directory) no-org))
(defun mwb-init-load-directory (rel-dir-name)
"Load up all the files using the init loaded from a directory"
(let* ((directory-name (expand-file-name rel-dir-name mwb-user-emacs-directory))
(files (directory-files
directory-name
nil
(rx-to-string '(seq any ".org" eol)))))
(dolist (f files)
(mwb-init--load (expand-file-name (file-name-sans-extension f) directory-name)))))
(when (>= emacs-major-version 27)
;; (load (concat (expand-file-name "alt/alt_init" mwb-user-emacs-directory) ".el"))
;;(load (concat (expand-file-name "alt/straight" mwb-user-emacs-directory) ".el"))
(mwb-init-load "config" "no-org"))