unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#11746: feature request: `isearch-query-replace' should open invisible text
@ 2012-06-19 17:56 Michael Heerdegen
  2012-06-19 21:15 ` Juri Linkov
  2013-02-14 19:02 ` Juri Linkov
  0 siblings, 2 replies; 8+ messages in thread
From: Michael Heerdegen @ 2012-06-19 17:56 UTC (permalink / raw)
  To: 11746

Hi,

if you have `search-invisible' non-nil, isearch "opens" invisible
text.  But when you hit M-% or C-M-% while searching, you loose this
ability: point will just be put inside invisible areas, and you don't
see what you're doing.  This is somewhat inconsistent.  Having an open
invisible feature for replacing text would be very convenient - think
of org files, for example.

Dunno if this would be easy to implement.  `isearch-query-replace'
uses just `perform-replace' from replace.el, which doesn't care about
invisible text.


Thanks,

Michael.




In GNU Emacs 24.1.50.1 (i486-pc-linux-gnu, GTK+ Version 3.4.2)
 of 2012-06-15 on zelenka, modified by Debian
 (emacs-snapshot package, version 2:20120615-1)
Windowing system distributor `The X.Org Foundation', version 11.0.11201902
Configured using:
 `configure '--build' 'i486-linux-gnu' '--host' 'i486-linux-gnu'
 '--prefix=/usr' '--sharedstatedir=/var/lib' '--libexecdir=/usr/lib'
 '--localstatedir=/var' '--infodir=/usr/share/info'
 '--mandir=/usr/share/man' '--with-pop=yes'
 '--enable-locallisppath=/etc/emacs-snapshot:/etc/emacs:/usr/local/share/emacs/24.1.50/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/24.1.50/site-lisp:/usr/share/emacs/site-lisp'
 '--without-compress-info' '--with-crt-dir=/usr/lib/i386-linux-gnu/'
 '--with-x=yes' '--with-x-toolkit=gtk3' '--with-imagemagick=yes'
 'build_alias=i486-linux-gnu' 'host_alias=i486-linux-gnu'
 'CFLAGS=-DDEBIAN -DSITELOAD_PURESIZE_EXTRA=5000 -g -O2' 'LDFLAGS=-g
 -Wl,--as-needed -znocombreloc' 'CPPFLAGS=-D_FORTIFY_SOURCE=2''






^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#11746: feature request: `isearch-query-replace' should open invisible text
  2012-06-19 17:56 bug#11746: feature request: `isearch-query-replace' should open invisible text Michael Heerdegen
@ 2012-06-19 21:15 ` Juri Linkov
  2013-02-14 19:02 ` Juri Linkov
  1 sibling, 0 replies; 8+ messages in thread
From: Juri Linkov @ 2012-06-19 21:15 UTC (permalink / raw)
  To: michael_heerdegen; +Cc: 11746

> Dunno if this would be easy to implement.  `isearch-query-replace'
> uses just `perform-replace' from replace.el, which doesn't care about
> invisible text.

A quick try does exactly what is needed:

=== modified file 'lisp/replace.el'
--- lisp/replace.el	2012-05-01 02:48:41 +0000
+++ lisp/replace.el	2012-06-19 21:14:59 +0000
@@ -1840,7 +1840,9 @@ (defun perform-replace (from-string repl
 					   limit t)
 				  ;; For speed, use only integers and
 				  ;; reuse the list used last time.
-				  (replace-match-data t real-match-data)))
+				  (prog1 (replace-match-data t real-match-data)
+				    (isearch-range-invisible
+				     (match-beginning 0) (match-end 0)))))
 				((and (< (1+ (point)) (point-max))
 				      (or (null limit)
 					  (< (1+ (point)) limit)))

But it would be better to implement this the same way as for isearch
filters.  Adding the same filters in replacements also will make the
variable `query-replace-skip-read-only' obsolete.





^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#11746: feature request: `isearch-query-replace' should open invisible text
  2012-06-19 17:56 bug#11746: feature request: `isearch-query-replace' should open invisible text Michael Heerdegen
  2012-06-19 21:15 ` Juri Linkov
@ 2013-02-14 19:02 ` Juri Linkov
  2013-02-14 20:17   ` Michael Heerdegen
  1 sibling, 1 reply; 8+ messages in thread
From: Juri Linkov @ 2013-02-14 19:02 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 11746

> if you have `search-invisible' non-nil, isearch "opens" invisible
> text.  But when you hit M-% or C-M-% while searching, you loose this
> ability: point will just be put inside invisible areas, and you don't
> see what you're doing.

The recent adaptation of isearch functions to `perform-replace'
makes it easy to implement this now.

Depending on the value of `search-invisible', if `open' then M-% or C-M-%
will open invisible overlays, if `nil' then they will skip invisible text,
and if `t' they will match replacements inside invisible areas like now.

The key point is using a search loop like in `isearch-search' that
either opens invisible overlays or skips them.

BTW, a similar loop could be later added to occur and hi-lock too,
but since in occur and hi-lock it makes no sense to open overlays,
they should just skip invisible areas like in `isearch-lazy-highlight-search'.

The patch below moves the search related part of code from `perform-replace'
to a new function `replace-search', adds a loop like in `isearch-search',
and moves handling of `query-replace-skip-read-only' to this loop.

It's worth to note that `wdired-isearch-filter-read-only' already has
exactly the same condition that checks for read-only-ness, so the
same condition is duplicated for modes that set both a read-only-skipping
isearch filter and `query-replace-skip-read-only', but this is not a problem.

=== modified file 'lisp/replace.el'
--- lisp/replace.el	2013-02-01 23:38:41 +0000
+++ lisp/replace.el	2013-02-14 18:55:14 +0000
@@ -1794,6 +1796,54 @@ (defvar replace-re-search-function nil
 It is called with three arguments, as if it were
 `re-search-forward'.")
 
+(defun replace-search (search-string limit regexp-flag delimited-flag
+				     case-fold-search)
+  "Search the next occurence of SEARCH-STRING to replace."
+  ;; Let-bind global isearch-* variables to values used
+  ;; to search the next replacement.  These let-bindings
+  ;; should be effective both at the time of calling
+  ;; `isearch-search-fun-default' and also at the
+  ;; time of funcalling `search-function'.
+  ;; These isearch-* bindings can't be placed higher
+  ;; outside of this function because then another I-search
+  ;; used after `recursive-edit' might override them.
+  (let* ((isearch-regexp regexp-flag)
+	 (isearch-word delimited-flag)
+	 (isearch-lax-whitespace
+	  replace-lax-whitespace)
+	 (isearch-regexp-lax-whitespace
+	  replace-regexp-lax-whitespace)
+	 (isearch-case-fold-search case-fold-search)
+	 (isearch-adjusted nil)
+	 (isearch-nonincremental t)	; don't use lax word mode
+	 (isearch-forward t)
+	 (search-function
+	  (or (if regexp-flag
+		  replace-re-search-function
+		replace-search-function)
+	      (isearch-search-fun-default)))
+	 (retry t)
+	 (success nil))
+    ;; Use a loop like in `isearch-search'.
+    (while retry
+      (setq success (funcall search-function search-string limit t))
+      ;; Clear RETRY unless the search predicate says
+      ;; to skip this search hit.
+      (if (or (not success)
+	      (and (run-hook-with-args-until-failure
+		    'isearch-filter-predicates
+		    (match-beginning 0) (match-end 0))
+		   (or (eq search-invisible t)
+		       (not (isearch-range-invisible
+			     (match-beginning 0) (match-end 0))))
+		   ;; Optionally ignore matches that have a read-only property.
+		   (or (not query-replace-skip-read-only)
+		       (not (text-property-not-all
+			     (match-beginning 0) (match-end 0)
+			     'read-only nil)))))
+	  (setq retry nil)))
+    success))
+
 (defun perform-replace (from-string replacements
 		        query-flag regexp-flag delimited-flag
 			&optional repeat-count map start end)
@@ -1881,29 +1931,6 @@ (defun perform-replace (from-string repl
 	;; Loop finding occurrences that perhaps should be replaced.
 	(while (and keep-going
 		    (not (or (eobp) (and limit (>= (point) limit))))
-		    ;; Let-bind global isearch-* variables to values used
-		    ;; to search the next replacement.  These let-bindings
-		    ;; should be effective both at the time of calling
-		    ;; `isearch-search-fun-default' and also at the
-		    ;; time of funcalling `search-function'.
-		    ;; These isearch-* bindings can't be placed higher
-		    ;; outside of this loop because then another I-search
-		    ;; used after `recursive-edit' might override them.
-		    (let* ((isearch-regexp regexp-flag)
-			   (isearch-word delimited-flag)
-			   (isearch-lax-whitespace
-			    replace-lax-whitespace)
-			   (isearch-regexp-lax-whitespace
-			    replace-regexp-lax-whitespace)
-			   (isearch-case-fold-search case-fold-search)
-			   (isearch-adjusted nil)
-			   (isearch-nonincremental t) ; don't use lax word mode
-			   (isearch-forward t)
-			   (search-function
-			    (or (if regexp-flag
-				    replace-re-search-function
-				  replace-search-function)
-				(isearch-search-fun-default))))
 		      ;; Use the next match if it is already known;
 		      ;; otherwise, search for a match after moving forward
 		      ;; one char if progress is required.
@@ -1916,8 +1943,9 @@ (defun perform-replace (from-string repl
 				  ;; adjacent match.
 				  (match-again
 				   (and
-				    (funcall search-function search-string
-					     limit t)
+				  (replace-search search-string limit
+						  regexp-flag delimited-flag
+						  case-fold-search)
 				    ;; For speed, use only integers and
 				    ;; reuse the list used last time.
 				    (replace-match-data t real-match-data)))
@@ -1930,13 +1958,12 @@ (defun perform-replace (from-string repl
 				   ;; if the search fails.
 				   (let ((opoint (point)))
 				     (forward-char 1)
-				     (if (funcall
-					  search-function search-string
-					  limit t)
-					 (replace-match-data
-					  t real-match-data)
+				   (if (replace-search search-string limit
+						       regexp-flag delimited-flag
+						       case-fold-search)
+				       (replace-match-data t real-match-data)
 				       (goto-char opoint)
-				       nil)))))))
+				     nil))))))
 
 	  ;; Record whether the match is nonempty, to avoid an infinite loop
 	  ;; repeatedly matching the same empty string.
@@ -1957,13 +1984,6 @@ (defun perform-replace (from-string repl
 			      (let ((match (match-data)))
 				(and (/= (nth 0 match) (nth 1 match))
 				     match))))))
-
-	  ;; Optionally ignore matches that have a read-only property.
-	  (unless (and query-replace-skip-read-only
-		       (text-property-not-all
-			(nth 0 real-match-data) (nth 1 real-match-data)
-			'read-only nil))
-
 	    ;; Calculate the replacement string, if necessary.
 	    (when replacements
 	      (set-match-data real-match-data)
@@ -2168,7 +2188,7 @@ (defun perform-replace (from-string repl
 				 (match-end 0)
 				 (current-buffer))
 			      (match-data t)))
-		      stack)))))
+		    stack))))
 
       (replace-dehighlight))
     (or unread-command-events






^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#11746: feature request: `isearch-query-replace' should open invisible text
  2013-02-14 19:02 ` Juri Linkov
@ 2013-02-14 20:17   ` Michael Heerdegen
  2013-02-15  7:54     ` Juri Linkov
  2013-05-27 23:04     ` Juri Linkov
  0 siblings, 2 replies; 8+ messages in thread
From: Michael Heerdegen @ 2013-02-14 20:17 UTC (permalink / raw)
  To: 11746

Juri Linkov <juri@jurta.org> writes:

> > if you have `search-invisible' non-nil, isearch "opens" invisible
> > text.  But when you hit M-% or C-M-% while searching, you loose this
> > ability: point will just be put inside invisible areas, and you don't
> > see what you're doing.
>
> The recent adaptation of isearch functions to `perform-replace'
> makes it easy to implement this now.

This sounds great.

I have a question about your patch: I can't find the definition of
`isearch-filter-predicates' you use - I find `isearch-filter-predicate'
however.  Is this a typo, or did I miss something?


Thanks,

Michael





^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#11746: feature request: `isearch-query-replace' should open invisible text
  2013-02-14 20:17   ` Michael Heerdegen
@ 2013-02-15  7:54     ` Juri Linkov
  2013-05-27 23:04     ` Juri Linkov
  1 sibling, 0 replies; 8+ messages in thread
From: Juri Linkov @ 2013-02-15  7:54 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 11746

> I have a question about your patch: I can't find the definition of
> `isearch-filter-predicates' you use - I find `isearch-filter-predicate'
> however.  Is this a typo, or did I miss something?

Sorry, that was from an uncommitted patch from bug#11378
where I couldn't decide what is the best way to handle both
the variable `search-invisible' and the invisibility isearch filter
simultaneously.  I'll summarize the current state in bug#11378.





^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#11746: feature request: `isearch-query-replace' should open invisible text
  2013-02-14 20:17   ` Michael Heerdegen
  2013-02-15  7:54     ` Juri Linkov
@ 2013-05-27 23:04     ` Juri Linkov
  2013-05-28 22:28       ` Juri Linkov
  1 sibling, 1 reply; 8+ messages in thread
From: Juri Linkov @ 2013-05-27 23:04 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 11746-done

>> > if you have `search-invisible' non-nil, isearch "opens" invisible
>> > text.  But when you hit M-% or C-M-% while searching, you loose this
>> > ability: point will just be put inside invisible areas, and you don't
>> > see what you're doing.
>>
>> The recent adaptation of isearch functions to `perform-replace'
>> makes it easy to implement this now.
>
> This sounds great.

Sorry for the delay, it required more testing with more fixes
like adding `isearch-clean-overlays' to `replace-dehighlight', etc.
This is installed now.





^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#11746: feature request: `isearch-query-replace' should open invisible text
  2013-05-27 23:04     ` Juri Linkov
@ 2013-05-28 22:28       ` Juri Linkov
  2013-05-30  0:03         ` Juri Linkov
  0 siblings, 1 reply; 8+ messages in thread
From: Juri Linkov @ 2013-05-28 22:28 UTC (permalink / raw)
  To: 11746; +Cc: michael_heerdegen

It would be nice to inform the users who customized
`search-invisible' to nil that now `query-replace'
ignores hidden matches, i.e. currently at the end
`query-replace' reports in the echo area:

  Replaced 2 occurrences

Instead of keeping silence about skipped occurrences
a better message would be:

  Replaced 2 occurrences (skipped 3 read-only, 4 invisible, 5 filtered out)

=== modified file 'lisp/replace.el'
--- lisp/replace.el	2013-05-27 23:38:56 +0000
+++ lisp/replace.el	2013-05-28 22:28:01 +0000
@@ -1934,6 +1965,9 @@ (defun perform-replace (from-string repl
          (keep-going t)
          (stack nil)
          (replace-count 0)
+         (skip-read-only-count 0)
+         (skip-filtered-count 0)
+         (skip-invisible-count 0)
          (nonempty-match nil)
 	 (multi-buffer nil)
 	 (recenter-last-op nil)	; Start cycling order with initial position.
@@ -2042,20 +2076,24 @@ (defun perform-replace (from-string repl
 				(and (/= (nth 0 match) (nth 1 match))
 				     match))))))
 
-	  ;; Optionally ignore matches that have a read-only property.
-	  (when (and (or (not query-replace-skip-read-only)
-			 (not (text-property-not-all
-			       (nth 0 real-match-data) (nth 1 real-match-data)
-			       'read-only nil)))
-		     ;; Optionally filter out matches.
-		     (run-hook-with-args-until-failure
-		      'isearch-filter-predicates
-		      (nth 0 real-match-data) (nth 1 real-match-data))
-		     ;; Optionally ignore invisible matches.
-		     (or (eq search-invisible t)
-			 (not (isearch-range-invisible
-			       (nth 0 real-match-data) (nth 1 real-match-data)))))
-
+	  (cond
+	   ;; Optionally ignore matches that have a read-only property.
+	   ((not (or (not query-replace-skip-read-only)
+		     (not (text-property-not-all
+			   (nth 0 real-match-data) (nth 1 real-match-data)
+			   'read-only nil))))
+	    (setq skip-read-only-count (1+ skip-read-only-count)))
+	   ;; Optionally filter out matches.
+	   ((not (run-hook-with-args-until-failure
+		  'isearch-filter-predicates
+		  (nth 0 real-match-data) (nth 1 real-match-data)))
+	    (setq skip-filtered-count (1+ skip-filtered-count)))
+	   ;; Optionally ignore invisible matches.
+	   ((not (or (eq search-invisible t)
+		     (not (isearch-range-invisible
+			   (nth 0 real-match-data) (nth 1 real-match-data)))))
+	    (setq skip-invisible-count (1+ skip-invisible-count)))
+	   (t
 	    ;; Calculate the replacement string, if necessary.
 	    (when replacements
 	      (set-match-data real-match-data)
@@ -2260,13 +2298,31 @@ (defun perform-replace (from-string repl
 				 (match-end 0)
 				 (current-buffer))
 			      (match-data t)))
-		      stack)))))
+		      stack))))))
 
       (replace-dehighlight))
     (or unread-command-events
-	(message "Replaced %d occurrence%s"
+	(message "Replaced %d occurrence%s%s"
 		 replace-count
-		 (if (= replace-count 1) "" "s")))
+		 (if (= replace-count 1) "" "s")
+		 (if (> (+ skip-read-only-count
+			   skip-filtered-count
+			   skip-invisible-count) 0)
+		     (format " (skipped %s)"
+			     (mapconcat
+			      'identity
+			      (delq nil (list
+					 (if (> skip-read-only-count 0)
+					     (format "%s read-only"
+						     skip-read-only-count))
+					 (if (> skip-invisible-count 0)
+					     (format "%s invisible"
+						     skip-invisible-count))
+					 (if (> skip-filtered-count 0)
+					     (format "%s filtered out"
+						     skip-filtered-count))))
+			      ", "))
+		   "")))
     (or (and keep-going stack) multi-buffer)))
 
 ;;; replace.el ends here





^ permalink raw reply	[flat|nested] 8+ messages in thread

* bug#11746: feature request: `isearch-query-replace' should open invisible text
  2013-05-28 22:28       ` Juri Linkov
@ 2013-05-30  0:03         ` Juri Linkov
  0 siblings, 0 replies; 8+ messages in thread
From: Juri Linkov @ 2013-05-30  0:03 UTC (permalink / raw)
  To: 11746; +Cc: michael_heerdegen

Actually opening hidden overlays is not optimal when the user types `!'
for automatic replacement.  There is no need to open overlays and
immediately close afterwards without seeing opened text by the user.
This can be improved with this patch:

=== modified file 'lisp/replace.el'
--- lisp/replace.el	2013-05-29 23:16:44 +0000
+++ lisp/replace.el	2013-05-30 00:00:39 +0000
@@ -2111,6 +2111,9 @@ (defun perform-replace (from-string repl
 	    (setq skip-filtered-count (1+ skip-filtered-count)))
 	   ;; Optionally ignore invisible matches.
 	   ((not (or (eq search-invisible t)
+		     ;; Don't open overlays for automatic replacements.
+		     (and (not query-flag) search-invisible)
+		     ;; Open hidden overlays for interactive replacements.
 		     (not (isearch-range-invisible
 			   (nth 0 real-match-data) (nth 1 real-match-data)))))
 	    (setq skip-invisible-count (1+ skip-invisible-count)))






^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2013-05-30  0:03 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-19 17:56 bug#11746: feature request: `isearch-query-replace' should open invisible text Michael Heerdegen
2012-06-19 21:15 ` Juri Linkov
2013-02-14 19:02 ` Juri Linkov
2013-02-14 20:17   ` Michael Heerdegen
2013-02-15  7:54     ` Juri Linkov
2013-05-27 23:04     ` Juri Linkov
2013-05-28 22:28       ` Juri Linkov
2013-05-30  0:03         ` Juri Linkov

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).