Last year I rewrote my configuration from scratch as I migrated from Spacemacs
to vanilla Emacs. For config style, I chose Org Babel - which normally involves
using a short Lisp file (init.el
) to tangle and load actual config in Org mode
(config.org
) - for its readability and easy navigation. After a while, however,
I find it not as handy as expected:
- At least 2 files are to be maintained, which adds unnecessary complexity
config.org
needs to be re-tangled for every single change, which gets slow as code blocks accumulated, and gets annoying with frequent restarts when I tried out new packages- Debugging is not straightforward. If I made any mistake (miss a parenthesis,
typo), error prompts will refer to locations in the tangled Lisp file
(
config.el
) instead of original Org file
I figured I don’t really need full-featured Org-mode (scheduling, deadlines,
agenda, etc..) for a simple init file. What I really need is easy cycling
navigation through the file, and, as I mainly use use-package
, the ability to
jump to a use-package
definition by name directly. After a little research, I
found that for the former outshine
comes to rescue, and for the latter,
use-package
provides a useful option use-package-enable-imenu-support
.
So here it is, a minimal skeleton for Emacs init.
;;; init.el --- skeleton config -*- lexical-binding: t; coding:utf-8; fill-column: 119 -*-
;;; Commentary:
;; A bare-boned config template. Use "outshine-cycle-buffer" (<Tab> and <S-Tab>
;; in org style) to navigate through sections, and "imenu" to locate individual
;; use-package definition.
;;; Bootstrap
;; Speed up startup
(setq gc-cons-threshold 402653184
gc-cons-percentage 0.6)
(add-hook 'after-init-hook
`(lambda ()
(setq gc-cons-threshold 800000
gc-cons-percentage 0.1)
(garbage-collect)) t)
;; Initialize package.el
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/"))
(package-initialize)
;; Bootstrap `use-package'
(setq-default use-package-always-ensure t ; Auto-download package if not exists
use-package-always-defer t ; Always defer load package to speed up startup
use-package-verbose nil ; Don't report loading details
use-package-expand-minimally t ; make the expanded code as minimal as possible
use-package-enable-imenu-support t) ; Let imenu finds use-package definitions
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(eval-when-compile
(require 'use-package))
;; Add system-wide defaults here, for example:
;;
;; (setq-default inhibit-startup-message t
;; initial-scratch-message nil)
;; Add all use-package definitions from here
(use-package outshine
;; Easier navigation for source files, especially this one.
:bind (:map outshine-mode-map
("<S-iso-lefttab>" . outshine-cycle-buffer)
)
:hook (emacs-lisp-mode . outshine-mode)
)
;; When config gets stable, using emacs server may be more convenient
;; (require 'server)
;; (unless (server-running-p)
;; (server-start))
;;; init.el ends here
Main features:
- Use
outshine
for cycling visibility in Org mode style - Use
imenu/counsel-imenu
to locate individual package configuration - Apply speed up techniques (modified from John Wiegley’s config)
From there I base all my configurations using use-package
, and have a startup
time of 0.9s with 100+ packages (Main trick: use :defer [N]
to defer-load all
non-necessary packages. The only must-haves for me are counsel
and org
).
Hope that’s useful for anyone thinking to rewrite their configs.
Update on 2019-05-30: My init file is published at yiufung/dot-emacs following the same idea.