“The mode-line that can be seen is not the eternal mode-line.” — Lao-Tzu,
Emacs 27 + edition
There was an age when every buffer carried a heavy belt of glyphs, digits and blinking widgets. Then a humble Lisp script shaved its head, sat down in the echo area and simply was. That script is Shaoline.
Shaoline replaces the traditional modeline with a minimalist, highly functional string drawn in the echo area. It vanishes the moment Emacs must speak, or stays if that is your Path. It does just enough and nothing more. Walk its Dao by doing nothing; everything is already done.
“When nothing is done, nothing remains undone.” — Tao Te Ching, §48
MELPA (package.el):
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(unless (package-installed-p 'shaoline)
(package-refresh-contents)
(package-install 'shaoline))
(require 'shaoline-mode)
(shaoline-mode 1)
(use-package shaoline
:straight (shaoline
:type git
:host github
:repo "11111000000/shaoline")
:init (require 'shaoline-mode) ; UI layer
:config (shaoline-mode 1)) ; enable globallyOr manual/local checkout:
;; Manual installation: clone/download and add to load-path
;; Example:
;; git clone https://github.com/11111000000/shaoline ~/.emacs.d/site-lisp/shaoline
;; If sources live under a 'lisp/' subdir, add that:
(add-to-list 'load-path "~/.emacs.d/site-lisp/shaoline/lisp")
(require 'shaoline-mode) ;; UI layer
(shaoline-mode 1) ;; enable globallyReady. Ignore the rest. Curious? Let insight flow further.
- Core (
shaoline.el) pure functions, no side-effects. - Effects (
shaoline-effects.el) the only gate to the world. - Strategy (
shaoline-strategy.el) yin / yang / adaptive logic. - UI (
shaoline-mode.el) turn on/off, reports. - Garden (
shaoline-segments.el) collection of pure segments.
Purity is kept, impurity is isolated – the metaphysics of performance.
- Echo-area only — no windows, frames, overlays.
- Four-layer architecture — Core → Effects → Strategy → UI.
- Strategies yin / yang / adaptive — auto-switch by load, file size, remote, GUI/TTY.
- EXWM tray aware — right margin auto-aligns under your system tray.
- Prefix-key hint — unfinished key sequences / C-u 42 show instantly.
- Token bucket — shapes update traffic, no storms.
- TTL cache + async for heavy segments (battery, project).
- Zero timers until truly needed — core stays timer-free.
- Persistent centre — last useful message lives until a new one appears.
- Dependency = 0 unless you want icons.
Student: “Master, how many dependencies does Shaoline have?” Master: “Mu.”
Tip: frequent external calls re-use the composed line thanks to
shaoline-compose-cache-ttl and shaoline-compose-min-interval.
(setq shaoline-segments
'((:left shaoline-segment-major-mode-icon
shaoline-segment-buffer-name
shaoline-segment-current-keys
shaoline-segment-modified)
(:center shaoline-segment-echo-message)
(:right shaoline-segment-position ; [line:col]
shaoline-segment-project-name
shaoline-segment-git-branch
shaoline-segment-battery
shaoline-segment-time)))Re-order, remove, or add functions as you wish.
| Variable | Default | Meaning |
|---|---|---|
| shaoline-mode-strategy | ‘yang | yin / yang / adaptive |
| shaoline-enable-dynamic-segments | t | Clock, battery & friends |
| shaoline-right-margin | 1 | Fixed right padding |
| shaoline-with-tray | t | Auto-adjust to EXWM system tray |
| shaoline-tray-refresh-interval | 1.5 | Seconds between tray margin recalculations |
| shaoline-hide-modeline | t | Hide the classic modeline |
| shaoline-update-debounce | 0.25 | Throttle cursor-movement refreshes |
| shaoline-cache-ttl | 2.0 | Default cache TTL (segments) |
| shaoline-compose-cache-ttl | 0.5 | TTL for full composed line cache |
| shaoline-compose-min-interval | 0.2 | Min interval between real recompositions |
| shaoline-debug | nil | Verbose logs in shaoline-logs |
Interactively: M-x customize-group RET shaoline RET
- Strategies – The Dao in Action
- yin — passive; no hooks/timers, manual
M-x shaoline-refresh. - yang — active; always visible, hides mode-line.
- adaptive — automatically chooses yin or yang.
Cycle live: M-x shaoline-toggle-strategy
| Segment | Shows | Needs |
|---|---|---|
| buffer-name | Buffer name | — |
| modified | “*” if buffer modified | — |
| current-keys | Current prefix / C-u 42 / “C-x” | — |
| position | Line (and column if asked) | — |
| major-mode-icon | Icon / text of major mode | all-the-icons (opt.) |
| project-name | Project name (TTL 2 s) | project.el / projectile |
| git-branch | Current Git branch | vc-git |
| battery | Percent + icon (async, TTL 5 s) | battery.el, async.el |
| time | 24 h clock | — |
| echo-message | Last non-empty (message …) | — |
More live in shaoline-segments.el or craft your own:
(shaoline-define-segment shaoline-segment-ts-lang ()
(when (fboundp 'treesit-language-at)
(format "%s" (treesit-language-at (point)))))
(push 'shaoline-segment-ts-lang (alist-get :left shaoline-segments))Running EXWM with a system tray? Shaoline measures its pixel width,
converts to characters and tweaks shaoline-right-margin on the fly.
Disable: (setq shaoline-with-tray nil)
Tune refresh: (setq shaoline-tray-refresh-interval 1.5)
Tune refresh: (setq shaoline-tray-refresh-interval 1.5)
- Where is my old modeline?
(setq shaoline-hide-modeline nil)or disable the mode. - Why does the line vanish on M-x? Minibuffer is talking, Shaoline bows.
- Echo area flickers!
Some package spams
(message nil). Turn onshaoline-debug. - TTY support? Yes; icons degrade to text, serenity remains.
- Force update? –
M-x shaoline-refresh. - Clear it now? –
M-x shaoline-clear.
Returning to Emptiness
(shaoline-total-cleanup) ;; removes timers, hooks, restores everything• Compose string < 0.2 ms (6 segments). • Timers start only when dynamic segments exist. • Token bucket shapes traffic; light-updates rate-limited further. • Memory footprint – the heaviest object is this README.
Documentation is a finger pointing at the moon; Shaoline shows both the moon and its phase.
MIT. Copy it, fork it, tie it to a kite and let it fly. —
Close this buffer, take a breath, return to code – nothing to add, nothing to remove.
