Here I have taken what @phils had posted and have modified it to:
- Take more
par formatting only on the region, without limiting to paragraph marking because
mark-paragraph relies on the major mode to work correctly.
- Attempt to make this work on code comments (by marking
comment-start characters as Quote characters for
- Attempt to handle
par errors more gracefully and try not to mess up the user's original buffer in the event of error
How to use the below code
- Evaluate the below code
M-h M-H if you want to fill the region in a paragraph
;; par - http://www.nicemice.net/par/
(when (executable-find "par")
(defun modi/par-fill-region (begin end par-option)
"Use `par' executable to fill region between BEGIN and END.
If PAR-OPTION is \\='(4) `\\[universal-argument]', also justify the text.
If PAR-OPTION is \\='(16) `\\[universal-argument] \\[universal-argument]', try to
make all lines of almost equal lengths instead of justifying.
See `man par' for more information."
(let* ((width-str (number-to-string fill-column))
(err-buf "*par Error*")
(concat "par "
"w" width-str " " ; set fill width
;; Body characters: . , ? upper-case lower-case
(shell-quote-argument "B=.,?_A_a") " "
;; Quote characters: space > | <comment-start char>
(concat "Q=_s>|" (when comment-start comment-start))) " "
;; Allow bodiless characters like *, space, .. to inc/dec by
;; up to 10 characters to adjust line width.
"T4" ; Expand tab chars to 4 spaces
;; Prefixes may not contain any trailing body characters, and
;; suffixes may not contain any leading body characters
"e" ; Expel/remove superfluous lines
(when (equal '(16) par-option)
"f") ; Try to make all lines of nearly the same length
"g" ; Make a better guess at inserting line breaks
(when (equal '(4) par-option)
"j") ; Justify the lines by inserting spaces between words
"q" ; Insert blank lines before/after quoted text
"R" ; Throw an error if a word length exceeds the fill width
"E" )) ; Send error to stderr instead of stdout
(before-text (buffer-substring-no-properties begin end))
;; Do the formatting in a temp buffer so that the text in the original
;; buffer doesn't get corrupted in case `par' fails due to some error.
(setq par-ret (shell-command-on-region
par-cmd nil :replace
(buffer-substring-no-properties (point-min) (point-max)))))
;; If 1 is returned, error occurred in the cmd execution; 0 - no error
(if (= 1 par-ret)
;; Switch to the error buffer
(special-mode)) ; Set this mode so that you can quit it quickly using C-u q
;; If no error occurred, do below in the original buffer
(delete-region begin end)
(message "Executed `%s' on the region" par-cmd)))
;; Rationale for the below binding is that it flows very well when selecting a
;; paragraph and then filling that region: "M-h M-H"
(global-set-key (kbd "M-H") #'modi/par-fill-region))