All Projects → zk-phi → setup

zk-phi / setup

Licence: other
Helper macros to write faster, portable and robust init script

Programming Languages

emacs lisp
2029 projects

Labels

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 installed
  • init.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.

Convert settings to setup- forms

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
  • Make init.el don’t stop even when bar-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

Helper tools to speed-up your init.el

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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].