After setting up leaf-keywords and Flycheck properly:
(straight-use-package 'leaf) (straight-use-package 'leaf-keywords) (require 'leaf) (require 'leaf-keywords) (leaf-keywords-init) (leaf flycheck :straight t :hook prog-mode-hook)
leaf-keywords being loaded, Flycheck will now return an error from leaf.el complaining that
:straight is an unrecognized keyword.
I tried putting
leaf-keywords-init in a
eval-when-compile or a
(cl-eval-when (compile load eval)) block, to no avail.
For the longest time I just turned Flycheck off in my init file (the only place where leaf.el is used), but yesterday I decided to take another look at it.
This is how Flycheck checks Emacs Lisp code (I only included the command part):
(flycheck-define-checker emacs-lisp "An Emacs Lisp syntax checker using the Emacs Lisp Byte compiler. See Info Node `(elisp)Byte Compilation'." :command ("emacs" (eval flycheck-emacs-args) (eval (let ((path (pcase flycheck-emacs-lisp-load-path (`inherit load-path) (p (seq-map #'expand-file-name p))))) (flycheck-prepend-with-option "--directory" path))) (option "--eval" flycheck-emacs-lisp-package-user-dir nil flycheck-option-emacs-lisp-package-user-dir) (option "--eval" flycheck-emacs-lisp-initialize-packages nil flycheck-option-emacs-lisp-package-initialize) (option "--eval" flycheck-emacs-lisp-check-declare nil flycheck-option-emacs-lisp-check-declare) "--eval" (eval (flycheck-emacs-lisp-bytecomp-config-form)) "--eval" (eval flycheck-emacs-lisp-check-form) "--" source-inplace) ;; ... )
This calls Emacs in a subprocess with:
flycheck-emacs-lisp-load-path(or the host's
load-pathif its value is
inherit) passed into
load-pathof the subprocess so that
- the flag for whether
declare-functionstatememts should be checked passed in
- the byte compiler set up to run in the right directory
Then finally it evaluates
flycheck-emacs-lisp-check-form, which has code that essentially byte-compiles the current buffer and reports the warnings and errors.
I suspected that leaf-keywords does not have a chance to be initialized in the subprocess, and… yeah. I'm not sure why
eval-when-compile didn't work though. Perhaps the “compile” in that context is different from what the byte compiler does?
Either way, this is the workaround I ended up with:
;; HACK: without this, the keywords added by leaf-keywords don't ;; take effect in the checker process, so they all trigger an ;; "unrecognized keyword" error. (setq flycheck-emacs-lisp-check-form (if (string-match-p "leaf-keywords" flycheck-emacs-lisp-check-form) ;; Don't do anything on subsequent evals flycheck-emacs-lisp-check-form (format "(progn %s %s)" '(progn (require 'leaf-keywords nil t) (leaf-keywords-init)) flycheck-emacs-lisp-check-form)))
leaf-keywords and initializes it right before the actual check happens.
It seems to me that changes to Emacs Lisp behavior — in this case the presence of features of a macro — should take place on load. It is analogous to a hypothetical package providing
pcase patterns; it would be similarly surprising if such a package doesn't activate those extensions on load.
But, as it stands right now, the workaround is good enough for me.