Subject: bug#63761: PATCH: Add commands for commenting/uncommenting rectangles

From: Okamsn <[email-masked]>
Date: Sat, 27 May 2023 22:06:20 +0000

Hello,

This patch adds commands for commenting rectangles and modifies 
`comment-dwim` to use them. This is useful, for example, for commenting 
out the middles of consecutive lines, for which `comment-region` 
wouldn't work. It does this by calling `comment-region` or 
`uncomment-region` on each segment within the rectangle. With this patch,

	word1 word2 word3
	word4 word5 word6

would become

	word1 /* word2 */ word3
	word4 /* word5 */ word6

instead of

	word1 /* word2 word3
	word4 word5 */ word6

as produced by `comment-region` when using `rectangle-mark-mode`.

Thank you.

From 633a84ec025a81dd4d8f7f18f5408c9a1698ff4c Mon Sep 17 00:00:00 2001
From: Earl Hyatt <[email-masked]>
Date: Sat, 27 May 2023 17:39:14 -0400
Subject: [PATCH] Add commands for commenting/uncommenting non-contiguous
 regions.

* lisp/newcomment.el (comment-noncontiguous-region)
(uncomment-noncontiguous-region, noncontiguous-comment-only-p)
(comment-or-uncomment-noncontiguous-region):
Add new commands for working on non-contiguous regions.

* lisp/newcomment.el (comment-dwim):
Use `comment-or-uncomment-noncontiguous-region' when region
is non-contiguous.
---
 lisp/newcomment.el | 64 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

diff --git a/lisp/newcomment.el b/lisp/newcomment.el
index 022bf3059be..95c02f1e491 100644
--- a/lisp/newcomment.el
+++ b/lisp/newcomment.el
@@ -923,6 +923,25 @@ uncomment-region
     (save-excursion
       (funcall uncomment-region-function beg end arg))))
 
+(defun uncomment-noncontiguous-region (region-bounds &optional arg)
+  "Call `uncomment-region' on each section of the noncontiguous region."
+  (interactive (progn
+                 (barf-if-buffer-read-only)
+                 (list (region-bounds) current-prefix-arg)))
+  (comment-normalize-vars)
+  (let ((markers (mapcar (pcase-lambda (`(,start . ,end))
+                           (cons (set-marker (make-marker) start)
+                                 (set-marker (make-marker) end)))
+                         region-bounds)))
+    (pcase-dolist (`(,start-m . ,end-m) markers)
+      (unless (= start-m end-m)
+        (uncomment-region (marker-position start-m)
+                          (marker-position end-m)
+                          arg)))
+    (pcase-dolist (`(,start-m . ,end-m) markers)
+      (set-marker start-m nil)
+      (set-marker end-m nil))))
+
 (defun uncomment-region-default-1 (beg end &optional arg)
   "Uncomment each line in the BEG .. END region.
 The numeric prefix ARG can specify a number of chars to remove from the
@@ -1235,6 +1254,25 @@ comment-region
     ;; FIXME: maybe we should call uncomment depending on ARG.
     (funcall comment-region-function beg end arg)))
 
+(defun comment-noncontiguous-region (region-bounds &optional arg)
+  "Call `comment-region' on each section of the noncontiguous region."
+  (interactive (progn
+                 (barf-if-buffer-read-only)
+                 (list (region-bounds) current-prefix-arg)))
+  (comment-normalize-vars)
+  (let ((markers (mapcar (pcase-lambda (`(,start . ,end))
+                           (cons (set-marker (make-marker) start)
+                                 (set-marker (make-marker) end)))
+                         region-bounds)))
+    (pcase-dolist (`(,start-m . ,end-m) markers)
+      (unless (= start-m end-m)
+        (comment-region (marker-position start-m)
+                        (marker-position end-m)
+                        arg)))
+    (pcase-dolist (`(,start-m . ,end-m) markers)
+      (set-marker start-m nil)
+      (set-marker end-m nil))))
+
 (defun comment-region-default-1 (beg end &optional arg)
   (let* ((numarg (prefix-numeric-value arg))
 	 (style (cdr (assoc comment-style comment-styles)))
@@ -1340,6 +1378,14 @@ comment-only-p
     (comment-forward (point-max))
     (<= end (point))))
 
+(defun noncontiguous-comment-only-p (noncontiguous-region-bounds)
+  "Return non-nil if the text on all lines of the region is all comments."
+  (let ((pass t))
+    (while (and pass noncontiguous-region-bounds)
+      (pcase-let ((`(,start . ,end) (pop noncontiguous-region-bounds)))
+        (setq pass (comment-only-p start end))))
+    pass))
+
 ;;;###autoload
 (defun comment-or-uncomment-region (beg end &optional arg)
   "Call `comment-region', unless the region only consists of comments,
@@ -1351,6 +1397,20 @@ comment-or-uncomment-region
 	       'uncomment-region 'comment-region)
 	   beg end arg))
 
+(defun comment-or-uncomment-noncontiguous-region (region-bounds &optional arg)
+  "Call `comment-noncontiguous-region', unless the region only
+consists of comments, in which case call `uncomment-noncontiguous-region'.
+If a prefix arg is given, it is passed on to the respective
+function."
+  (interactive (progn
+                 (barf-if-buffer-read-only)
+                 (list (region-bounds) current-prefix-arg)))
+  (comment-normalize-vars)
+  (funcall (if (noncontiguous-comment-only-p region-bounds)
+	       #'uncomment-noncontiguous-region
+             #'comment-noncontiguous-region)
+	   region-bounds arg))
+
 ;;;###autoload
 (defun comment-dwim (arg)
   "Call the comment command you want (Do What I Mean).
@@ -1365,7 +1425,9 @@ comment-dwim
   (interactive "*P")
   (comment-normalize-vars)
   (if (use-region-p)
-      (comment-or-uncomment-region (region-beginning) (region-end) arg)
+      (if (region-noncontiguous-p)
+          (comment-or-uncomment-noncontiguous-region (region-bounds) arg)
+        (comment-or-uncomment-region (region-beginning) (region-end) arg))
     (if (save-excursion (beginning-of-line) (not (looking-at "\\s-*$")))
 	;; FIXME: If there's no comment to kill on this line and ARG is
 	;; specified, calling comment-kill is not very clever.
-- 
2.34.1