Emacs: Reformat Lines for Source Code 🚀

By Xah Lee. Date: . Last updated: .

Here's a command to reformat current paragraph into 1 long line or multiple short lines.

This command is designed to work with programing language source code. It strictly exchange whitespaces and newline characters.

(defun xah-reformat-lines (&optional Width)
  "Reformat current block or selection into short lines or 1 long line.
When called for the first time, change to one line. Second call change it to multi-lines. Repeated call toggles.
If `universal-argument' is called first, ask user to type max length of line. By default, it is 66.

Note: this command is different from emacs `fill-region' or `fill-paragraph'.
This command never adds or delete non-whitespace chars. It only exchange whitespace sequence.

URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html'
Created 2016 or before.
Version: 2021-07-05 2021-08-13 2022-03-12 2022-05-16 2022-12-24"
  (interactive)
  ;; This symbol has a property 'is-long-p, the possible values are t and nil. This property is used to easily determine whether to compact or uncompact, when this command is called again
  (let (xisLong xwidth xp1 xp2)
    (setq xwidth (if Width Width (if current-prefix-arg (prefix-numeric-value current-prefix-arg) 66)))
    (setq xisLong (if (eq last-command this-command) (get this-command 'is-long-p) nil))
    (let ((xbds (xah-get-bounds-of-block-or-region))) (setq xp1 (car xbds) xp2 (cdr xbds)))
    (if current-prefix-arg
        (xah-reformat-to-multi-lines xp1 xp2 xwidth)
      (if xisLong
          (xah-reformat-to-multi-lines xp1 xp2 xwidth)
        (progn
          (xah-reformat-whitespaces-to-one-space xp1 xp2))))
    (put this-command 'is-long-p (not xisLong))))

(defun xah-reformat-to-multi-lines ( &optional Begin End MinLength)
  "Replace spaces by a newline at ~70 chars, on current block or selection.
If `universal-argument' is called first, ask user for max width.

URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html'
Version: 2018-12-16 2021-07-06 2021-08-12"
  (interactive)
  (let ( xp1 xp2 xminlen )
    (setq xminlen (if MinLength MinLength (if current-prefix-arg (prefix-numeric-value current-prefix-arg) fill-column)))
    (if (and Begin End)
        (setq xp1 Begin xp2 End)
      (let ((xbds (xah-get-bounds-of-block-or-region))) (setq xp1 (car xbds) xp2 (cdr xbds))))
    (save-excursion
      (save-restriction
        (narrow-to-region xp1 xp2)
        (goto-char (point-min))
        (while (re-search-forward " +" nil :move)
          (when (> (- (point) (line-beginning-position)) xminlen)
            (replace-match "\n" )))))))

(defun xah-reformat-whitespaces-to-one-space (Begin End)
  "Replace whitespaces by one space.

URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html'
Version: 2017-01-11 2022-01-08"
  (interactive "r")
  (save-restriction
      (narrow-to-region Begin End)
      (goto-char (point-min))
      (while (search-forward "\n" nil :move) (replace-match " "))
      (goto-char (point-min))
      (while (search-forward "\t" nil :move) (replace-match " "))
      (goto-char (point-min))
      (while (re-search-forward " +" nil :move) (replace-match " "))
      (goto-char (point-max))))

(defun xah-get-bounds-of-block ()
  "Return the boundary (START . END) of current block.

URL `http://xahlee.info/emacs/emacs/elisp_get-selection-or-unit.html'
Version: 2021-08-12"
  (let (xp1 xp2 (xblankRegex "\n[ \t]*\n"))
    (save-excursion
      (setq xp1 (if (re-search-backward xblankRegex nil 1)
                    (goto-char (match-end 0))
                  (point)))
      (setq xp2 (if (re-search-forward xblankRegex nil 1)
                    (match-beginning 0)
                  (point))))
    (cons xp1 xp2)))

(defun xah-get-bounds-of-block-or-region ()
  "If region is active, return its boundary, else same as `xah-get-bounds-of-block'.

URL `http://xahlee.info/emacs/emacs/elisp_get-selection-or-unit.html'
Version: 2021-08-12"
  (if (region-active-p)
      (cons (region-beginning) (region-end))
    (xah-get-bounds-of-block)))

When working on programing language code, we want to reformat lines by a exchange of whitespaces.

The advantage of the code above is that they do not call emacs fill-region commands.

fill-region sometimes delete non-whitespace characters.

fill-region sometimes remove whitespaces to none, or create whitespaces.

Emacs fill-region Problems

Here's examples of fill-region issues.

whitespaces added

- Xxxx xx xxxxxxxxx xxxxxxx xxxxxxx xxxx xxxxxxx xx xxxxxxxxxx xxxxxxxx xxxx, xxx xxx'x xxxx xxxxxxx xxxx xxx xxxxx xxxx xxxxxxxxxxxx xxxxx xxxx xxxx xxxxxx. Xxx xxxx-xxxxxx xxxxxxxx xxxxxxxxx xxxx xxx xxxxxx xx xxxxx xxxx, xx xxxxxx xxxxxx xxxxxxx xxxxxx x xxxxx xx xxxxx.

after fill-paragraph.

adaptive-fill-mode is t

becomes

- Xxxx xx xxxxxxxxx xxxxxxx xxxxxxx xxxx xxxxxxx xx xxxxxxxxxx
  xxxxxxxx xxxx, xxx xxx'x xxxx xxxxxxx xxxx xxx xxxxx xxxx
  xxxxxxxxxxxx xxxxx xxxx xxxx xxxxxx. Xxx xxxx-xxxxxx xxxxxxxx
  xxxxxxxxx xxxx xxx xxxxxx xx xxxxx xxxx, xx xxxxxx xxxxxx xxxxxxx
  xxxxxx x xxxxx xx xxxxx.

Notice the extra whitespaces.

if adaptive-fill-mode is nil, then this won't happen.

adaptive-fill-mode is t by default.

Asterisks Deleted

* Xxxx xx xxxxxxxxx xxxxxxx xxxxxxx xxxx xxxxxxx xx xxxxxxxxxx
* xxxxxxxx xxxx, xxx xxx'x xxxx xxxxxxx xxxx xxx xxxxx xxxx
* xxxxxxxxxxxx xxxxx xxxx xxxx xxxxxx. Xxx xxxx-xxxxxx xxxxxxxx
* xxxxxxxxx xxxx xxx xxxxxx xx xxxxx xxxx, xx xxxxxx xxxxxx xxxxxxx
* xxxxxx x xxxxx xx xxxxx.

after fill-paragraph with big fill-column value.

adaptive-fill-mode is t

becomes

* Xxxx xx xxxxxxxxx xxxxxxx xxxxxxx xxxx xxxxxxx xx xxxxxxxxxx xxxxxxxx xxxx, xxx xxx'x xxxx xxxxxxx xxxx xxx xxxxx xxxx xxxxxxxxxxxx xxxxx xxxx xxxx xxxxxx. Xxx xxxx-xxxxxx xxxxxxxx xxxxxxxxx xxxx xxx xxxxxx xx xxxxx xxxx, xx xxxxxx xxxxxx xxxxxxx xxxxxx x xxxxx xx xxxxx.

The asterisks are gone.

if adaptive-fill-mode is nil, then this won't happen.

Whitespace Created When There Was None

混沌未分天地乱,茫茫渺渺无人见。自从盘古破鸿蒙,开辟从兹清浊辨。覆载群生仰至仁,发明万物皆成善。欲知造化会元功,须看西游释厄传。一派白虹起,千寻雪浪飞;海风吹不断,江月照还依。冷气分青嶂,馀流润翠微;潺湲名瀑布,真似挂帘帷。

after fill-paragraph.

becomes

混沌未分天地乱,茫茫渺渺无人见。自从盘古破鸿蒙,开辟从兹清浊辨。覆载群
生仰至仁,发明万物皆成善。欲知造化会元功,须看西游释厄传。一派白虹起,
千寻雪浪飞;海风吹不断,江月照还依。冷气分青嶂,馀流润翠微;潺湲名瀑布,
真似挂帘帷。

whitespaces are created at positions when there was none.

This is a problem when displayed in browser. Because, the newline characters will create a gap, and gaps in Chinese can change meaning.

Emacs Lines, Column, Cursor Position

Soft-Wrap Lines

Reformat Lines (Hard-Wrap)

Show Line Number, Column Number

Highlight Current Line, Screen Line