From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Juri Linkov Newsgroups: gmane.emacs.bugs Subject: bug#19829: 25.0.50; query-replace in rectangle regions do not honor boundaries Date: Thu, 19 Feb 2015 21:12:52 +0200 Organization: LINKOV.NET Message-ID: <877fvdu34b.fsf@mail.linkov.net> References: <87r3txhkz1.fsf@ceis-strat.com> <87twyt5o8q.fsf@mail.linkov.net> <874mqsq6ar.fsf@mail.linkov.net> <87zj8iljfk.fsf@mail.linkov.net> <87r3tnax8h.fsf@mail.linkov.net> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: ger.gmane.org 1424373621 31151 80.91.229.3 (19 Feb 2015 19:20:21 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 19 Feb 2015 19:20:21 +0000 (UTC) Cc: Bastien , 19829@debbugs.gnu.org To: Stefan Monnier Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Thu Feb 19 20:20:13 2015 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1YOWdw-0008NX-Q9 for geb-bug-gnu-emacs@m.gmane.org; Thu, 19 Feb 2015 20:20:13 +0100 Original-Received: from localhost ([::1]:57776 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YOWdw-0004zj-4A for geb-bug-gnu-emacs@m.gmane.org; Thu, 19 Feb 2015 14:20:12 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:33986) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YOWdr-0004wL-3B for bug-gnu-emacs@gnu.org; Thu, 19 Feb 2015 14:20:08 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YOWdn-0007i1-OS for bug-gnu-emacs@gnu.org; Thu, 19 Feb 2015 14:20:07 -0500 Original-Received: from debbugs.gnu.org ([140.186.70.43]:57702) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YOWdn-0007hn-LR for bug-gnu-emacs@gnu.org; Thu, 19 Feb 2015 14:20:03 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1YOWdn-0007go-7a for bug-gnu-emacs@gnu.org; Thu, 19 Feb 2015 14:20:03 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Juri Linkov Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 19 Feb 2015 19:20:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 19829 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 19829-submit@debbugs.gnu.org id=B19829.142437354629454 (code B ref 19829); Thu, 19 Feb 2015 19:20:03 +0000 Original-Received: (at 19829) by debbugs.gnu.org; 19 Feb 2015 19:19:06 +0000 Original-Received: from localhost ([127.0.0.1]:48935 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YOWcr-0007ez-6Q for submit@debbugs.gnu.org; Thu, 19 Feb 2015 14:19:05 -0500 Original-Received: from ps18281.dreamhost.com ([69.163.222.226]:39731 helo=ps18281.dreamhostps.com) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1YOWcp-0007el-9N for 19829@debbugs.gnu.org; Thu, 19 Feb 2015 14:19:04 -0500 Original-Received: from localhost.linkov.net (ps18281.dreamhostps.com [69.163.222.226]) by ps18281.dreamhostps.com (Postfix) with ESMTP id B6CB231DB17EF8; Thu, 19 Feb 2015 11:19:01 -0800 (PST) In-Reply-To: (Stefan Monnier's message of "Wed, 18 Feb 2015 17:56:21 -0500") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.0.50 (x86_64-pc-linux-gnu) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 140.186.70.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:99613 Archived-At: >> + (lambda (delete &optional positions) > > No: additional *value*, not additional *argument*. Sorry, I misread your previous message. Then maybe the argument `delete' should be renamed to something else, but I have no idea for another name. >> + (let (start end) >> + (move-to-column startcol) >> + (setq start (point)) >> + (move-to-column endcol) >> + (setq end (point)) >> + (setcdr positions (cons (cons start end) (cdr positions))))) > > Aka > (move-to-column startcol) > (let ((start (point))) > (move-to-column endcol) > (push (cons start (point)) (cdr positions))) > >> + (apply-on-rectangle 'extract-rectangle-position > ^^^ > #' Done in the new patch. >> + ;; Use local binding in add-function below. >> + (isearch-filter-predicate isearch-filter-predicate) >> + (rectangular-region-positions nil) > > replace.el should not know about rectangles and shouldn't check > rectangle-mark-mode. It should simply always call > region-extract-function to get the various elements that make up the > "region". This is true generally, but query-replace is a very special command. It already does many unorthodox things like putting markers on region boundaries, and then deactivating the mark. What is more, it needs to use rectangle boundaries in isearch-filter-predicate, so lazy-highlight will match only strings it's going to replace. I propose to start with simpler commands that operate on region, and add support for rectangular regions to them to see how this feature develops, and then optimize query-replace based on this experience. diff --git a/lisp/simple.el b/lisp/simple.el index 25293ed..76d84c9 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -958,13 +958,19 @@ (defcustom delete-active-region t (defvar region-extract-function (lambda (delete) (when (region-beginning) - (if (eq delete 'delete-only) - (delete-region (region-beginning) (region-end)) - (filter-buffer-substring (region-beginning) (region-end) delete)))) + (cond + ((eq delete 'positions) + (list (cons (region-beginning) (region-end)))) + ((eq delete 'delete-only) + (delete-region (region-beginning) (region-end))) + (t + (filter-buffer-substring (region-beginning) (region-end) delete))))) "Function to get the region's content. Called with one argument DELETE. If DELETE is `delete-only', then only delete the region and the return value is undefined. If DELETE is nil, just return the content as a string. +If DELETE is `positions', then don't delete, but just return the +positions of the region as a list of (START . END) boundaries. If anything else, delete the region and return its content as a string.") (defun delete-backward-char (n &optional killflag) diff --git a/lisp/rect.el b/lisp/rect.el index c5a5486..271f720 100644 --- a/lisp/rect.el +++ b/lisp/rect.el @@ -257,6 +257,19 @@ (defun extract-rectangle (start end) (apply-on-rectangle 'extract-rectangle-line start end lines) (nreverse (cdr lines)))) +(defun extract-rectangle-positions (start end) + "Return the positions of the rectangle with corners at START and END. +Return it as a list of (START . END) boundaries, one for each line of +the rectangle." + (let (positions) + (apply-on-rectangle + (lambda (startcol endcol) + (move-to-column startcol) + (push (cons (prog1 (point) (move-to-column endcol)) (point)) + positions)) + start end) + (nreverse positions))) + (defvar killed-rectangle nil "Rectangle for `yank-rectangle' to insert.") @@ -681,8 +694,12 @@ (defun rectangle-previous-line (&optional n) (defun rectangle--extract-region (orig &optional delete) - (if (not rectangle-mark-mode) - (funcall orig delete) + (cond + ((not rectangle-mark-mode) + (funcall orig delete)) + ((eq delete 'positions) + (extract-rectangle-positions (region-beginning) (region-end))) + (t (let* ((strs (funcall (if delete #'delete-extract-rectangle #'extract-rectangle) @@ -696,7 +713,7 @@ (defun rectangle--extract-region (orig &optional delete) (put-text-property 0 (length str) 'yank-handler `(rectangle--insert-for-yank ,strs t) str) - str)))) + str))))) (defun rectangle--insert-for-yank (strs) (push (point) buffer-undo-list) diff --git a/lisp/emulation/cua-rect.el b/lisp/emulation/cua-rect.el index ea8b524..044939f 100644 --- a/lisp/emulation/cua-rect.el +++ b/lisp/emulation/cua-rect.el @@ -666,6 +666,22 @@ (defun cua--extract-rectangle () (setq rect (cons row rect)))))) (nreverse rect))) +(defun cua--extract-rectangle-positions () + (let (rect) + (if (not (cua--rectangle-virtual-edges)) + (cua--rectangle-operation nil nil nil nil nil ; do not tabify + (lambda (s e _l _r) + (setq rect (cons (cons s e) rect)))) + (cua--rectangle-operation nil 1 nil nil nil ; do not tabify + (lambda (s e l r _v) + (goto-char s) + (move-to-column l) + (setq s (point)) + (move-to-column r) + (setq e (point)) + (setq rect (cons (cons s e) rect))))) + (nreverse rect))) + (defun cua--insert-rectangle (rect &optional below paste-column line-count) ;; Insert rectangle as insert-rectangle, but don't set mark and exit with ;; point at either next to top right or below bottom left corner @@ -1405,8 +1421,12 @@ (defun cua--rectangle-highlight-for-redisplay (orig &rest args) (defun cua--rectangle-region-extract (orig &optional delete) (cond - ((not cua--rectangle) (funcall orig delete)) - ((eq delete 'delete-only) (cua--delete-rectangle)) + ((not cua--rectangle) + (funcall orig delete)) + ((eq delete 'positions) + (cua--extract-rectangle-positions)) + ((eq delete 'delete-only) + (cua--delete-rectangle)) (t (let* ((strs (cua--extract-rectangle)) (str (mapconcat #'identity strs "\n"))) diff --git a/lisp/replace.el b/lisp/replace.el index e0636e0..f2fee8c 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -2089,6 +2089,9 @@ (defun perform-replace (from-string replacements ;; If non-nil, it is marker saying where in the buffer to stop. (limit nil) + ;; Use local binding in add-function below. + (isearch-filter-predicate isearch-filter-predicate) + (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 +2104,24 @@ (defun perform-replace (from-string replacements "Query replacing %s with %s: (\\\\[help] for help) ") minibuffer-prompt-properties)))) + ;; If rectangle is active, operate on rectangular region. + (when (and (boundp 'rectangle-mark-mode) rectangle-mark-mode) + (setq rectangular-region-positions + (mapcar (lambda (position) + (cons (copy-marker (car position)) + (copy-marker (cdr position)))) + (funcall region-extract-function 'positions))) + (add-function :after-while 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