I am trying to include support for smartparens in a major-mode package. I want that if a user is using smartparens, then he will get additional pair definitions, and if he isn't, he will not notice anything different.
For that, I am using instructions of the form:
(declare-function sp-with-modes "ext:smartparens.el" t t)
(declare-function sp-local-pair "ext:smartparens.el" t t)
(eval-after-load 'smartparens
'(sp-with-modes '(test-mode)
(sp-local-pair "<" ">"
:actions '(insert wrap autoskip))))
This works fine if the file is not byte-compiled, or if it is byte-compiled by an emacs with smartparens loaded, or with emacs 24.3. However, with 24.5 (and probably with 24.4, though I cannot easily test it at the moment), and if it is compiled in a vanilla emacs (for example with cask build
), it gives an error, stating that the argument (insert wrap autoskip)
is not one of the keywords admissible by sp-local-pair
(:actions
...).
If I remove the call to sp-with-modes
, and I replace the definition with
(sp-local-pair 'test-mode "<" ">"
:actions '(insert wrap autoskip))
then it works fine. The macro sp-with-modes
is supposed to loop over its argument (a list), inserting each element as first argument to everything in the body, I suspect that it confuses the byte-compiler at some point.
Is there a way to include this kind of "maybe evaluation" in a byte-compiled file, without confusing the byte-compiler? For example, is it possible to tell the compiler "do not compile this form"?
Or what would be the idiomatic way of achieving this kind of third-party support in a package?
Please note that the question involves smartparens
because that's where I noticed the problem, but I suspect that it may happen with a lot of other packages.
The rest of the question is a MWE. I assumes that your regular emacs configuration has smartparens
installed.
File ~/.emacs.d/tests/test.el
(declare-function sp-with-modes "ext:smartparens.el" t t)
(declare-function sp-local-pair "ext:smartparens.el" t t)
(eval-after-load 'smartparens
'(sp-with-modes '(test-mode)
(sp-local-pair "<" ">"
:actions '(insert wrap autoskip))))
(define-derived-mode test-mode
fundamental-mode "Test" "Test mode")
(provide 'test)
File ~/.emacs.d/tests/init.el
:
(require 'package)
(setq package-enable-at-startup nil)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
(package-initialize)
(add-to-list 'load-path "~/.emacs.d/tests/")
(require 'test)
(require 'smartparens)
(smartparens-global-mode t)
Run emacs -Q
and evaluate
(byte-compile-file "~/.emacs.d/tests/test.el")
then run emacs -Q --load "~/.emacs.d/tests/init.el"
.
expand-region
package when adding a functionsave-mark-and-excursion
that was introduced in emacs 25.0: Does a similar approach of usingeval-when-compile
andfboundp
work here?eval-after-load
has a compiler-macro which expandsquote
d forms into a lambda-form, which is then compiled (This could be viewed as a bug.). Since the compiler does not know, thatsp-with-modes
is a macro, it treats it as function, I believe. When the code gets evaluated, it'll be to late for any macro exansion andsp-local-pair
is eval'd as the first argument to the other function...