Emacs: Reformat Lines for Source Code 🚀
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.
- A whitespace here is defined to be one of {space, tab, newline} character.
- A whitespace sequence (WS), is 1 or more consecutive whitespace.
- Any WS is considered equivalent to any other.
- The only change allowed is swapping one WS by another.
- A WS is never created if it didn't exist before.
- A WS is never deleted.
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.