19 Apr 2019

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.