;;; 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