setup.el
高速・安全な設定ファイルを書くためのマクロ集
Helper macros to write faster, portable and robust init script
Description
With this package, you can implement more portable and robust init script:
init.el
no longer dies even if some of required packages are NOT installedinit.el
no longer dies even if some settings throw errors
This package is a bunch of Emacs Lisp MACROS (not functions) and tries
to complete as much computations during compile as it can, which makes
init.el
drastically faster when byte-compiled.
Installation
In the first lines of your init.el
, load setup
during compile:
(eval-when-compile
(require 'setup))
and initialize.
(setup-initialize)
You may also want to load setup.el
when editing init.el
for better
indentation and highlighting.
(with-eval-after-load 'lisp-mode
(require 'setup))
Note that you need to byte-compile init.el
to unleash the power of
setup.el
.
You should use M-x setup-byte-compile-file
to byte-compile
init.el
, which launches a clean Emacs environment and compile with
the instance. This makes compile-time conditional branches (discussed
later) work properly.
setup-
forms
Convert settings to setup-lazy
If you find a package NOT required during startup, convert the setting as follows:
;; BEFORE
(require 'foo)
(foo-initialize)
(global-set-key (kbd "C-x C-x") 'run-foo)
;; AFTER
(setup-lazy '(run-foo) "foo"
:prepare (global-set-key (kbd "C-x C-x") 'run-foo)
(foo-initialize))
This delays loading of package foo
until run-foo
is invoked. And
execute foo-initialize
immediately after foo
is loaded (before
proceeding to run-foo
).
Following packages typically are NOT required during startup:
- Plug-in packages for a specific language mode
- Application packages (web browser, for example) you don’t use immediately after startup
setup-lazy
actually does some more things for you. See the setup
section below.
setup-in-idle
If a lazy-loaded package is heavy to load, you may eager-load when Emacs is in idle state (user don’t do anything for a specific duration).
(setup-in-idle "foo")
(setup-lazy '(run-foo) "foo"
...)
The duration may be configured via setup-idle-threshold
(in
seconds).
(eval-when-compile
(setq setup-idle-threshold 10))
setup
If you find a package IS required during startup, convert the setting as follows:
;; BEFORE
(require 'bar)
(bar-global-mode)
;; AFTER
(setup "bar"
(bar-global-mode))
This basically doesn’t change the semantics, but does the following:
- Resolve
load-path
DURING COMPILE for performance- If the package is not found DURING COMPILE, skip loading it and
invoking
bar-global-mode
to avoid errors
- If the package is not found DURING COMPILE, skip loading it and
invoking
- Make
init.el
don’t stop even whenbar-global-mode
throws an error - (Optionally) Track and echo loading time of the package
Note that load-path
search is done DURING COMPILE, this makes
startup process no slower (but even a bit faster).
If you don’t want the time-tracking, then you may disable with
setup-silent
variable.
(eval-when-compile
(setq setup-silent t))
setup-after
If a setting depends on two packages and both packages are delayed
with setup-lazy
, you may use setup-after
to setup safely.
(setup-lazy '(run-foo) "foo"
...)
(setup-lazy '(super-perl-mode) "super-perl"
:prepare (add-hook 'perl-mode-hook 'super-perl-mode)
;; THIS RUNS AFTER BOTH "foo" AND "super-perl" ARE LOADED
(setup-after "foo"
(make-foo-collaborate-with-super-perl-mode)))
setup-expecting
and setup-fallback
setup-expecting
is executed only when the package is found in the
load-path
(during compile).
This may be useful to make a package be prepared for another lazy-loaded package.
(setup-lazy '(run-foo) "foo"
...)
(setup "bar"
(setup-expecting "foo"
(prepare-bar-for-foo)))
setup-fallback
is the opposite: executed only when the package is
NOT found.
Compile-time execution utils
!
If there’s any pure (cacheable) but time-consuming computation, it should be computed during compile.
setup.el
provides macro !
, which evaluates the body during
compile.
(defconst my-super-constant (! (my-pure-function)))
!if
, !when
, !unless
, !cond
, !case
If there’s any conditional branch, whose condition never changes across sessions, then it should be branched during compile.
setup.el
provides compile-time versions of conditional statements.
Typical usecase is for OS-specific settings:
(defun my-open-file (file)
(!cond ((eq system-type 'windows-nt)
(w32-shell-execute "open" file))
((eq system-type 'darwin)
(shell-command (concat "open '" file "'")))
(t
(error "unsupported system"))))
!foreach
If there’s any dolist
loop over a constant list, then it should be
unrolled during compile.
setup.el
provides compile-time version of dolist
.
(!foreach '(narrow-to-region
dired-find-alternate-file
upcase-region
downcase-region)
(put ,it 'disabled nil))
Note that element value can be referred with =,it=.
Speed-up hacks
Disable GC
You may disable GC temporarilly to make the startup process faster.
setup.el
do that for you when setup-enable-gc-threshold-hacks
is enabled.
(eval-when-compile
(setq setup-enable-gc-threshold-hacks t))
Disable file-name handlers
You may disable file-name handlers to make startup process faster.
They are used to implement built-in “Magic File Name” feature, but this makes it slower to load packages.
Since the feature is (usually) not used during startup, setup.el
temporarilly disables the feature for you, if
setup-disable-magic-file-name
is enabled.
(eval-when-compile
(setq setup-disable-magic-file-name t))
Other LISP utils
Pseudo-async execution
You may wrap some forms with !-
so that it runs
pseudo-asynchronously (runs with slight intervals controlled by a
timer) to avoid blocking user input.
(!-
(setup 'foo
...))
You may customize the interval with setup-delay-interval
(in seconds).
(eval-when-compile
(setq setup-delay-interval 0.5))
If you want suppress messages caused by delayed settings, you may set
variable setup-delay-silent
.
(eval-when-compile
(setq setup-delay-silent t))
setup-keybinds
Batch bind keys. You may also use conditional binding:
;; nil means the global-map
(setup-keybinds nil
"C-l" 'recenter
;; use "pager" commands if it exists (during compile).
;; otherwise use built-in commands instead.
"C-u" '("pager" pager-page-up scroll-down)
"C-v" '("pager" pager-page-down scroll-up))
;; specify keymap name to modify local keybinds
(setup-keybinds Buffer-menu-mode-map
"RET" 'Buffer-menu-select
"SPC" 'Buffer-menu-delete
"d" 'Buffer-menu-execute)
setup-hook
Like add-hook
but
- doesn’t need
lambda
form - skips duplication check (for performance)
(setup-hook 'eshell-mode-hook
(setq eshell-last-command-status 0))
:oneshot
hooks, which runs only once, are also supported
(setup-hook 'eshell-mode-hook
:oneshot
(local-set-key ...))
setup-with-delayed-redisplay
Avoid redisplaying the screen while executing body for performance:
(setup-with-delayed-redisplay
(mini-modeline-mode 1))
setup-silently
Suppress message while executing body for performance:
(setup-silently
(key-chord-mode 1)) ; the message "Key Chord mode on" is suppressed
init.el
Helper tools to speed-up your Profiler integration
You may enable profiler to find time-consuming functions in your
init.el
.
(eval-when-compile
(setq setup-use-profiler t))
setup.el
shows the result after the setup process is completed.
Load-history tracker
You may enable load-history tracker to inspect how and when packages are loaded.
(eval-when-compile
(setq setup-use-load-history-tracker t))
setup.el
shows the result after the setup process is completed.