diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-18 20:35:55 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-18 20:35:55 -0500 |
| commit | 8f56aced97f128b6b4d4dcf19fe5c1ba43447e6b (patch) | |
| tree | 8a69c253de56affa260755635d7a63ad58ecf906 /scripts/theme-studio/face-coverage-dump.el | |
| parent | e64ad7a99c7636a14a60ec6d92551ea3a98b2ec6 (diff) | |
| download | dotemacs-8f56aced97f128b6b4d4dcf19fe5c1ba43447e6b.tar.gz dotemacs-8f56aced97f128b6b4d4dcf19fe5c1ba43447e6b.zip | |
feat(theme-studio): add reproducible face-coverage generator and diff
face-coverage.org was rebuilt by a throwaway /tmp script each time. This makes it reproducible: face-coverage-dump.el dumps every face's name, docstring, and defface file from the live daemon (plus all group docs and package summaries), and face_coverage.py turns that into the tiered worklist (emacs-core / emacs-general / per-package), classifying each face by where its defface lives. make face-coverage regenerates the file; make face-coverage-diff reports the coverage delta against the committed copy.
The dump binds coding-system-for-write so writing the docstring JSON never drops into the interactive coding-system prompt. I validated the builder by regenerating and diffing against the hand-built worklist: headings identical, only the intro and one sharper description differ.
Diffstat (limited to 'scripts/theme-studio/face-coverage-dump.el')
| -rw-r--r-- | scripts/theme-studio/face-coverage-dump.el | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/scripts/theme-studio/face-coverage-dump.el b/scripts/theme-studio/face-coverage-dump.el new file mode 100644 index 000000000..6fc73469f --- /dev/null +++ b/scripts/theme-studio/face-coverage-dump.el @@ -0,0 +1,51 @@ +;;; face-coverage-dump.el --- Dump face/group/package data for the coverage worklist -*- lexical-binding: t -*- + +;;; Commentary: +;; Emits a JSON file that face_coverage.py consumes to build face-coverage.org. +;; For every face in `face-list' it records the name, its documentation string, +;; and the file its `defface' lives in (used to classify built-in vs package). +;; It also dumps every customization group's documentation and every elpa +;; package's summary, so the builder can describe each bucket offline. +;; +;; Run against a live daemon to capture actually-loaded packages: +;; emacsclient -e '(progn (load ".../face-coverage-dump.el") +;; (face-coverage-dump "/tmp/face-coverage-data.json"))' +;; or on a clean checkout via `emacs --batch -l init.el' then the same calls +;; (lazily-loaded packages will be absent until required). + +;;; Code: + +(require 'json) +(require 'package) + +(defun face-coverage-dump (outfile) + "Write face, group, and package data as JSON to OUTFILE." + (let ((faces nil) + (groups (make-hash-table :test 'equal)) + (packages (make-hash-table :test 'equal))) + (dolist (f (face-list)) + (push (vector (symbol-name f) + (or (face-documentation f) :null) + (or (symbol-file f 'defface) :null)) + faces)) + (mapatoms + (lambda (s) + (let ((d (get s 'group-documentation))) + (when (stringp d) (puthash (symbol-name s) d groups))))) + (when (boundp 'package-alist) + (dolist (entry package-alist) + (let ((sum (ignore-errors (package-desc-summary (cadr entry))))) + (when (stringp sum) (puthash (symbol-name (car entry)) sum packages))))) + ;; Docstrings carry curly quotes and other non-ASCII; bind the write coding + ;; system so `with-temp-file' never drops into the interactive + ;; select-safe-coding-system prompt (which pops in the daemon's frame). + (let ((n (length faces)) + (coding-system-for-write 'utf-8-unix)) + (with-temp-file outfile + (insert (json-serialize (list :faces (vconcat (nreverse faces)) + :groups groups + :packages packages)))) + (message "face-coverage-dump: %d faces -> %s" n outfile)))) + +(provide 'face-coverage-dump) +;;; face-coverage-dump.el ends here |
