Walls of Text

2 min read Original article ↗

The basic idea is that, once we partition an Emacs frame into an appropriate number of windows, each representing one column, and show the same buffer in all of them, we can use follow-mode to do the work of maintaining the effect of a unified columnar display. The basic mechanism is

(defvar *wall-num-columns* 3)
(make-variable-buffer-local '*wall-num-columns*)

(defun wall (buffer &optional num-columns)
  (interactive "bbuffer:")
  (delete-other-windows)
  (follow-mode 0)
  (setf *wall-num-columns* (or num-columns *wall-num-columns*))
  (dotimes (i (1- *wall-num-columns*))
    (split-window-right)
    (balance-windows)
    (other-window 1)
    (switch-to-buffer buffer))
  (other-window 1)
  (balance-windows)
  (wall-minor-mode))

After working with this for a bit, I realized that Emacs's default settings for paging were not to my liking; with the following in effect, each Page-Up or Page-Down results a more predictable movement of one full column:

(setf scroll-preserve-screen-position t)
(setf next-screen-context-lines 0)

Sometimes it's handy to move by entire screens-worth of text:

(defun wall-next-screen ()
  (interactive)
  (dotimes (i (length (follow-all-followers)))
    (scroll-up-command)))

(defun wall-prev-screen ()
  (interactive)
  (dotimes (i (length (follow-all-followers)))
    (scroll-down-command)))

And sometimes, it's worth changing the number of columns. The use of follow-all-followers accounts for those times when not every window in the frame is part of the text wall.

(defun wall-add-column ()
  (interactive)
  (wall (current-buffer) (1+ (length (follow-all-followers)))))

(defun wall-remove-column ()
  (interactive)
  (wall (current-buffer) (max 1 (1- (length (follow-all-followers))))))

(defun wall-reset ()
  (interactive) 
  (wall (current-buffer) 3))

Now, to tie everything together as a minor mode. I have found that the volume wheel on my keyboard makes a nice control for whenever I want to increase or decrease other things in an ad hoc way.

(define-minor-mode wall-minor-mode
  "Turn any buffer into a wall of text."
  :lighter "wall"
  :keymap (list
	   (cons (kbd "M-<XF86AudioLowerVolume>") 'wall-remove-column)
	   (cons (kbd "M-<XF86AudioRaiseVolume>") 'wall-add-column)
	   (cons (kbd "C-<next>") 'wall-next-screen)
	   (cons (kbd "C-<prior>") 'wall-prev-screen))
  (follow-mode 'toggle))

;; This lets me wallify any buffer at a moment's notice.
(global-set-key (kbd "<M-XF86AudioMute>") 'wall-reset)