Subject: bug#19829: 25.0.50; query-replace in rectangle regions do not honor boundaries

From: Juri Linkov <[email-masked]>
Date: Fri, 13 Feb 2015 02:54:55 +0200

>> I wonder how do other commands that accept region-beginning/end
>> process the rectangular region?
>
> Well, there's the source code to help you find out, of course.
> More seriously, the rectangle support (can be rect.el or cua-rect.el)
> code sets up `region-extract-function' and commands that need to operate
> on the region should then call `region-extract-function' instead of
> relying on region-beginning/end.
>
> Obviously, this infrastructure is not sufficient for query-replace, so
> we'll need to add a `region-map-function` or something like that.

Better would be to add a function that will return a list of positions
of the rectangular region, i.e. while currently for the contiguous region
region-beginning/end returns its bounds, for non-contiguous rectangular
regions we need a list of column bounds.

As a proof of concept, this small patch implements the feature of
query-replace in rectangular regions, but I'm not sure how its parts
should be refactored into a more general function such as e.g.
`region-positions-extract-function' that will return a list of
region positions.

diff --git a/lisp/replace.el b/lisp/replace.el
index e0636e0..8eeeb1a 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -2089,6 +2089,7 @@ (defun perform-replace (from-string replacements
 
          ;; If non-nil, it is marker saying where in the buffer to stop.
          (limit nil)
+	 (rectangular-region-positions nil)
 
          ;; Data for the next match.  If a cons, it has the same format as
          ;; (match-data); otherwise it is t if a match is possible at point.
@@ -2101,6 +2102,27 @@ (defun perform-replace (from-string replacements
                       "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) ")
                      minibuffer-prompt-properties))))
 
+    (when rectangle-mark-mode
+      (let ((positions (list nil)))
+	(apply-on-rectangle
+	 (lambda (startcol endcol positions)
+	   (setcdr positions (cons (cons
+				    (progn (move-to-column startcol) (point))
+				    (progn (move-to-column endcol)   (point)))
+				   (cdr positions))))
+	 (region-beginning) (region-end) positions)
+	(setq rectangular-region-positions (nreverse (cdr positions)))
+	(add-function :after-while (local 'isearch-filter-predicate)
+		      (lambda (start end)
+			(delq nil (mapcar
+				   (lambda (positions)
+				     (and
+				      (>= start (car positions))
+				      (<= start (cdr positions))
+				      (>= end   (car positions))
+				      (<= end   (cdr positions))))
+				   rectangular-region-positions))))))
+
     ;; If region is active, in Transient Mark mode, operate on region.
     (if backward
 	(when end