doom/repos/x-lib/box-comment.el

132 lines
4.9 KiB
EmacsLisp

;;; box-comment.el -*- lexical-binding: t -*-
;;
;;; Variables:
(defvar box-comment--comment-start-emacs-lisp-mode ";; "
"String to begin comment lines in `emacs-lisp-mode'.")
(defvar box-comment--comment-start-lisp-interaction-mode ";; "
"String to begin comment lines in `list-interaction-mode'.")
(defvar box-comment--comment-start-scheme-mode ";; "
"String to begin comment lines in `scheme-mode'.")
;;
;;; Code:
(defun +x-box-comment--comment-start ()
"Get the string that starts box comments for the current mode."
(let ((box-comment-var
(intern (format "box-comment--comment-start-%s" major-mode))))
(string-trim-right (if (boundp box-comment-var)
(eval box-comment-var)
comment-start)
"[[:space:]]")))
(defun +x-box-comment--box-comment-line-p (box-comment-start)
"Return t if point is in a line suitable for box comments."
(save-excursion
(end-of-line)
(search-backward (string-trim-right box-comment-start "[[:space:]]")
(line-beginning-position)
t)))
(defun +x-box-comment--box-comment-boundary-p (box-comment-start)
"Return non-nil if point is in a box comment boundary line."
(save-excursion
(if (/= (- (line-end-position) (line-beginning-position)) fill-column)
nil
(end-of-line)
(re-search-backward (format "^[[:space:]]*%s -+$" box-comment-start)
(line-beginning-position)
t))))
(defun +x-box-comment--insert-box-boundary (box-comment-start indent len)
"Insert a box comment boundary line."
(indent-line-to indent)
(insert box-comment-start " " (make-string len ?-)))
(defun +x-box-comment--re-search (re beg end)
"Return non-nil if RE is between BEG and END."
(save-excursion
(goto-char beg)
(re-search-forward re end t)))
;;;###autoload
(defun +x-box-comment/box-comment ()
"Format the comment at point into a box comment.
If the comment at point is already a box comment, this command
will update and fix the box comment format (if necessary)."
(interactive)
(let ((box-comment-start (+x-box-comment--comment-start))
(use-region (use-region-p))
offset region-len indent len)
;; If we are not in a comment, create a comment.
(unless (+x-box-comment--box-comment-line-p box-comment-start)
(if (and (not use-region)
(+x-box-comment--re-search "^[[:space:]]*$"
(line-beginning-position)
(line-end-position)))
(comment-dwim nil)
(comment-line nil)
;; `comment-line' behaves differently whether its invoked on a region or
;; not. In a region, the point ends up within the comment, unless the
;; region ended on an empty line in which case the point ends up on the
;; next line. Outside of a region, the point ends up on the next line.
(if use-region
(progn
(unless (or (+x-box-comment--box-comment-line-p box-comment-start)
(not (+x-box-comment--re-search box-comment-start
(region-beginning)
(region-end))))
(forward-line -1))
(end-of-line)
(setq region-len (- (region-end) (region-beginning))))
(forward-line -1)
(end-of-line))))
(save-excursion
(end-of-line)
(when (search-backward box-comment-start (line-beginning-position) t)
;; Get the variables.
(setq indent (current-column))
(setq len (- (- fill-column (+ (length box-comment-start) 1)) indent))
;; Find the first line.
(if use-region
(progn
(goto-char (region-beginning))
(setq offset (- (current-column) indent)))
(while (+x-box-comment--box-comment-line-p box-comment-start)
(forward-line -1))
(forward-line +1))
;; If not a box comment boundary, insert it.
(unless (+x-box-comment--box-comment-boundary-p box-comment-start)
(beginning-of-line)
(+x-box-comment--insert-box-boundary box-comment-start indent len)
(newline)
(indent-line-to indent))
;; Now find the last line
(if use-region
(forward-char (+ offset region-len))
(while (+x-box-comment--box-comment-line-p box-comment-start)
(forward-line +1))
(forward-line -1))
;; If not a box comment boundary, insert it.
(unless (+x-box-comment--box-comment-boundary-p box-comment-start)
(end-of-line)
(newline)
(+x-box-comment--insert-box-boundary box-comment-start
indent
len))))))
(provide 'box-comment)
;;; box-comment.el ends here