(defun has-face-at-point (face &optional position)
(interactive)
(when (null position) (setq position (point)))
(unless (consp face) (setq face (list face)))
(let ((props (text-properties-at position)))
(loop for (key value) on props by #'cddr
do (when (and (eql key 'face) (member value face))
(return t)))))
(defun face-start (face)
(save-excursion
(while (and (has-face-at-point face) (not (bolp)))
(backward-char))
(- (point) (save-excursion (move-beginning-of-line 1)) (if (bolp) 0 -1))))
(defun R-beautify-comments ()
(interactive)
;; Because this function does a lot of insertion, it would
;; be better to execute it in the temporary buffer, while
;; copying the original text of the file into it, such as
;; to prevent junk in the formatted buffer's history
(let ((content (buffer-string))
(comments '(font-lock-comment-face font-lock-comment-delimiter-face)))
(with-temp-buffer
(insert content)
(save-excursion
(goto-char (point-min))
;; thingatpt breaks if there are overlays with their own faces
(let* ((commentp (has-face-at-point 'font-lock-comment-face))
(margin
(if commentp (face-start comments) 0))
assumed-margin pre-comment commented-lines)
(while (not (eobp))
(move-end-of-line 1)
;; (message "has comment: %s" (has-face-at-point 'font-lock-comment-face))
(cond
((and (has-face-at-point comments)
commentp) ; this is a comment continued from
; the previous line
(setq assumed-margin (face-start comments)
pre-comment
(buffer-substring-no-properties
(save-excursion (move-beginning-of-line 1))
(save-excursion (move-beginning-of-line 1)
(forward-char assumed-margin) (point))))
(message "pre-comment <%s> <- %d" pre-comment assumed-margin)
(if (every
(lambda (c) (or (char-equal c ?\ ) (char-equal c ?\t)))
pre-comment)
;; This is the comment preceded by whitespace
(setq commentp nil margin 0 commented-lines 0)
(message "Continued comment %d <> %d" assumed-margin margin)
(if (< assumed-margin margin)
;; The comment found starts on the left of
;; the margin of the comments found so far
(save-excursion
(move-beginning-of-line 1)
(forward-char assumed-margin)
(message "correcting indentation by: %d in line: <%s>"
(- margin assumed-margin)
(buffer-substring-no-properties
(save-excursion
(move-beginning-of-line 1))
(save-excursion
(move-end-of-line 1) (point))))
(insert (make-string (- margin assumed-margin) ?\ ))
(incf commented-lines))
;; This could be optimized by going forward and
;; collecting as many comments there are, but
;; it is simpler to return and re-indent comments
;; (assuming there won't be many such cases anyway.
(setq margin assumed-margin)
(move-end-of-line (- commented-lines)))))
((has-face-at-point comments)
;; This is the fresh comment
;; This entire block needs refactoring, it is
;; a repetition of the half the previous block
(setq assumed-margin (face-start comments)
pre-comment
(buffer-substring-no-properties
(save-excursion (move-beginning-of-line 1))
(save-excursion (move-beginning-of-line 1)
(forward-char assumed-margin) (point))))
;; (message "pre-comment <%s> <- %d" pre-comment assumed-margin)
(unless (every
(lambda (c)
(or (char-equal c ?\ ) (char-equal c ?\t)))
pre-comment)
(setq commentp t margin assumed-margin commented-lines 0)))
(commentp
;; This is the line directly after a block of comments
(setq commentp nil margin assumed-margin commented-lines 0))
(t ; This is the line with no comments
(message "test") ; left here for testing purposes
;; (message "line: <%s>"
;; (buffer-substring-no-properties
;; (save-excursion
;; (move-beginning-of-line 1))
;; (save-excursion
;; (move-end-of-line 1) (point))))
))
(unless (eobp) (forward-char))
;; (message "outside while: <%s>"
;; (buffer-substring-no-properties
;; (save-excursion
;; (move-beginning-of-line 1))
;; (save-excursion
;; (move-end-of-line 1) (point))))
)
;; Retrieve back the formatted contnent
(setq content (buffer-string)))))
(erase-buffer)
(insert content)))