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.

When there's a text selection, work on that region. Else, work on current text block (text blank lines.).

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

The main command is xah-reformat-lines.

These commands do not depend on emacs fill commands.

(defun xah-reformat-lines ( &optional @width)
  "Reformat current text block or selection into short lines or 1 long line.

When called for the first time, change to one long line. Second call change it to multiple short lines. Repeated call toggles.

If `universal-argument' is called first, use the number value for min length of line. By default, it's 70.

URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html'
Created 2016 or before.
Version 2021-07-05"
  (interactive)
  ;; This command symbol has a property “'isLong-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 ( isLong-p $blanksRegex $p1 $p2 )
    (setq @width (if @width
                     @width
                   (if current-prefix-arg
                       (prefix-numeric-value current-prefix-arg) 70 )))
    (setq isLong-p
          (if (eq last-command this-command)
              (get this-command 'isLong-p)
            nil))
    (setq $blanksRegex "\n[ \t]*\n")
    (if (use-region-p)
        (setq $p1 (region-beginning) $p2 (region-end))
      (save-excursion
        (if (re-search-backward $blanksRegex nil "move")
            (progn (re-search-forward $blanksRegex)
                   (setq $p1 (point)))
          (setq $p1 (point)))
        (if (re-search-forward $blanksRegex nil "move")
            (progn (re-search-backward $blanksRegex)
                   (setq $p2 (point)))
          (setq $p2 (point)))))
    (progn
      (if current-prefix-arg
          (xah-reformat-to-multi-lines $p1 $p2 @width)
        (if isLong-p
            (xah-reformat-to-multi-lines $p1 $p2 @width)
          (xah-reformat-whitespaces-to-one-space $p1 $p2)))
      (put this-command 'isLong-p (not isLong-p)))))
(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"
  (interactive "r")
  (save-excursion
    (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 " ")))))
(defun xah-reformat-to-multi-lines ( &optional @begin @end @min-length)
  "Replace spaces by a newline at places so lines are not long.
When there is a text selection, act on the selection, else, act on a text block separated by blank lines.

If `universal-argument' is called first, use the number value for min length of line. By default, it's 70.

URL `http://xahlee.info/emacs/emacs/emacs_reformat_lines.html'
Version 2018-12-16 2020-09-08"
  (interactive)
  (let (
        $p1 $p2
        ($blanks-regex "\n[ \t]*\n")
        ($minlen (if @min-length
                     @min-length
                   (if current-prefix-arg (prefix-numeric-value current-prefix-arg) fill-column))))
    (if (and  @begin @end)
        (setq $p1 @begin $p2 @end)
      (if (use-region-p)
          (progn (setq $p1 (region-beginning) $p2 (region-end)))
        (save-excursion
          (if (re-search-backward $blanks-regex nil "move")
              (progn (re-search-forward $blanks-regex)
                     (setq $p1 (point)))
            (setq $p1 (point)))
          (if (re-search-forward $blanks-regex nil "move")
              (progn (re-search-backward $blanks-regex)
                     (setq $p2 (point)))
            (setq $p2 (point))))))
    (save-excursion
      (save-restriction
        (narrow-to-region $p1 $p2)
        (goto-char (point-min))
        (while
            (re-search-forward " +" nil "move")
          (when (> (- (point) (line-beginning-position)) $minlen)
            (replace-match "\n" )))))))

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.

Line Wrap