]> git.eshelyaron.com Git - emacs.git/commitdiff
Add search function to search within filenames in Dired and WDired (bug#14013)
authorJuri Linkov <juri@linkov.net>
Mon, 28 Mar 2022 18:00:32 +0000 (21:00 +0300)
committerJuri Linkov <juri@linkov.net>
Mon, 28 Mar 2022 18:00:32 +0000 (21:00 +0300)
* lisp/dired-aux.el (dired-isearch-filenames-mode): Use
dired-isearch-search-filenames on isearch-search-fun-function
instead of dired-isearch-filter-filenames on isearch-filter-predicate.
(dired-isearch-filter-filenames): Remove function.
(dired-isearch-search-filenames): Add function.

* lisp/isearch.el (isearch-message-prefix): Add isearch-search-fun-function
to the list of supported advice-functions along with isearch-filter-predicate.

* lisp/replace.el (replace-search): Add comment.

* lisp/wdired.el (wdired-search-replace-filenames): New defcustom.
(wdired-isearch-filter-read-only): Remove function.
(wdired-change-to-wdired-mode, wdired-change-to-dired-mode):
Add and remove dired-isearch-search-filenames on isearch-search-fun-function
instead of wdired-isearch-filter-read-only on isearch-filter-predicate.
Also set/unset replace-search-function and replace-re-search-function.
Remove and restore isearch-mode-hook with dired-isearch-filenames-setup.
The problem is that dired-isearch-filenames-setup adds
dired-isearch-filenames-end to isearch-mode-end-hook that removes
dired-isearch-search-filenames added to isearch-search-fun-function
in wdired-change-to-wdired-mode.  Then replace-highlight can't use
dired-isearch-search-filenames.

lisp/dired-aux.el
lisp/isearch.el
lisp/replace.el
lisp/wdired.el

index 956899c20511a11deeed43ace4a69cf9d7d72375..c49e4e91d83f6493c17063a3b8a47ffa53129dfc 100644 (file)
@@ -3155,16 +3155,16 @@ a file name.  Otherwise, it searches the whole buffer without restrictions."
 
 (define-minor-mode dired-isearch-filenames-mode
   "Toggle file names searching on or off.
-When on, Isearch skips matches outside file names using the predicate
-`dired-isearch-filter-filenames' that matches only at file names.
-When off, it uses the original predicate."
+When on, Isearch skips matches outside file names using the search function
+`dired-isearch-search-filenames' that matches only at file names.
+When off, it uses the default search function."
   :lighter nil
   (if dired-isearch-filenames-mode
-      (add-function :before-while (local 'isearch-filter-predicate)
-                    #'dired-isearch-filter-filenames
+      (add-function :around (local 'isearch-search-fun-function)
+                    #'dired-isearch-search-filenames
                     '((isearch-message-prefix . "filename ")))
-    (remove-function (local 'isearch-filter-predicate)
-                     #'dired-isearch-filter-filenames))
+    (remove-function (local 'isearch-search-fun-function)
+                     #'dired-isearch-search-filenames))
   (when isearch-mode
     (setq isearch-success t isearch-adjusted t)
     (isearch-update)))
@@ -3188,12 +3188,46 @@ Intended to be added to `isearch-mode-hook'."
   (unless isearch-suspended
     (kill-local-variable 'dired-isearch-filenames)))
 
-(defun dired-isearch-filter-filenames (beg end)
-  "Test whether some part of the current search match is inside a file name.
-This function returns non-nil if some part of the text between BEG and END
-is part of a file name (i.e., has the text property `dired-filename')."
-  (text-property-not-all (min beg end) (max beg end)
-                        'dired-filename nil))
+(defun dired-isearch-search-filenames (orig-fun)
+  "Return the function that searches inside file names.
+The returned function narrows the search to match the search string
+only as part of a file name enclosed by the text property `dired-filename'.
+It's intended to override the default search function."
+  (let ((search-fun (funcall orig-fun))
+        (property 'dired-filename))
+    (lambda (string &optional bound noerror count)
+      (let* ((old (point))
+             ;; Check if point is already on the property.
+             (beg (when (get-text-property
+                         (if isearch-forward old (max (1- old) (point-min)))
+                         property)
+                    old))
+             end found)
+        ;; Otherwise, try to search for the next property.
+        (unless beg
+          (setq beg (if isearch-forward
+                        (next-single-property-change old property)
+                      (previous-single-property-change old property)))
+          (when beg (goto-char beg)))
+        ;; Non-nil `beg' means there are more properties.
+        (while (and beg (not found))
+          ;; Search for the end of the current property.
+          (setq end (if isearch-forward
+                        (next-single-property-change beg property)
+                      (previous-single-property-change beg property)))
+          (setq found (funcall
+                       search-fun string (if bound (if isearch-forward
+                                                       (min bound end)
+                                                     (max bound end))
+                                           end)
+                       noerror count))
+          (unless found
+            (setq beg (if isearch-forward
+                          (next-single-property-change end property)
+                        (previous-single-property-change end property)))
+            (when beg (goto-char beg))))
+        (unless found (goto-char old))
+        found))))
 
 ;;;###autoload
 (defun dired-isearch-filenames ()
index 05a73edead7a4f0cc02ae900336d9e945df309ae..956b115ce42c0ae3f5353d3f49e0513b8eea09c8 100644 (file)
@@ -3457,11 +3457,13 @@ the word mode."
                    (if (and (not isearch-success) (not isearch-case-fold-search))
                        "case-sensitive ")
                    (let ((prefix ""))
-                     (advice-function-mapc
-                      (lambda (_ props)
-                        (let ((np (cdr (assq 'isearch-message-prefix props))))
-                          (if np (setq prefix (concat np prefix)))))
-                      isearch-filter-predicate)
+                     (dolist (advice-function (list isearch-filter-predicate
+                                                    isearch-search-fun-function))
+                       (advice-function-mapc
+                        (lambda (_ props)
+                          (let ((np (cdr (assq 'isearch-message-prefix props))))
+                            (if np (setq prefix (concat np prefix)))))
+                        advice-function))
                      prefix)
                    (isearch--describe-regexp-mode isearch-regexp-function)
                   (cond
index 06be597855488afa5406294445ca99e71656e358..e6f565d8024b40aef467f1a82c78fda9df06255d 100644 (file)
@@ -2685,6 +2685,11 @@ to a regexp that is actually used for the search.")
          (or (if regexp-flag
                  replace-re-search-function
                replace-search-function)
+              ;; `isearch-search-fun' can't be used here because
+              ;; when buffer-local `isearch-search-fun-function'
+              ;; searches e.g. the minibuffer history, then
+              ;; `query-replace' should not operate on the whole
+              ;; history, but only on the minibuffer contents.
              (isearch-search-fun-default))))
     (funcall search-function search-string limit t)))
 
index ab3b91bbe55e0d51adada65c7781b6873433b2a5..d2a6bad0f283abfe62e3c9c25934e51a2fd690bf 100644 (file)
@@ -155,6 +155,11 @@ nonexistent directory will fail."
   :version "26.1"
   :type 'boolean)
 
+(defcustom wdired-search-replace-filenames t
+  "Non-nil to search and replace in file names only."
+  :version "29.1"
+  :type 'boolean)
+
 (defvar-keymap wdired-mode-map
   :doc "Keymap used in `wdired-mode'."
   "C-x C-s" #'wdired-finish-edit
@@ -217,6 +222,7 @@ symbolic link targets, and filenames permission."
   (error "This mode can be enabled only by `wdired-change-to-wdired-mode'"))
 (put 'wdired-mode 'mode-class 'special)
 
+(declare-function dired-isearch-search-filenames "dired-aux")
 
 ;;;###autoload
 (defun wdired-change-to-wdired-mode ()
@@ -237,9 +243,16 @@ See `wdired-mode'."
               (dired-remember-marks (point-min) (point-max)))
   (setq-local wdired--old-point (point))
   (wdired--set-permission-bounds)
-  (setq-local query-replace-skip-read-only t)
-  (add-function :after-while (local 'isearch-filter-predicate)
-                #'wdired-isearch-filter-read-only)
+  (when wdired-search-replace-filenames
+    (add-function :around (local 'isearch-search-fun-function)
+                  #'dired-isearch-search-filenames
+                  '((isearch-message-prefix . "filename ")))
+    (setq-local replace-search-function
+                (setq-local replace-re-search-function
+                            (funcall isearch-search-fun-function)))
+    ;; Original dired hook removes dired-isearch-search-filenames that
+    ;; is needed outside isearch for lazy-highlighting in query-replace.
+    (remove-hook 'isearch-mode-hook #'dired-isearch-filenames-setup t))
   (use-local-map wdired-mode-map)
   (force-mode-line-update)
   (setq buffer-read-only nil)
@@ -319,11 +332,6 @@ or \\[wdired-abort-changes] to abort changes")))
             ;; Is this good enough? Assumes no extra white lines from dired.
             (put-text-property (1- (point-max)) (point-max) 'read-only t)))))))
 
-(defun wdired-isearch-filter-read-only (beg end)
-  "Skip matches that have a read-only property."
-  (not (text-property-not-all (min beg end) (max beg end)
-                             'read-only nil)))
-
 ;; Protect the buffer so only the filenames can be changed, and put
 ;; properties so filenames (old and new) can be easily found.
 (defun wdired--preprocess-files ()
@@ -438,8 +446,13 @@ non-nil means return old filename."
     (remove-text-properties
      (point-min) (point-max)
      '(front-sticky nil rear-nonsticky nil read-only nil keymap nil)))
-  (remove-function (local 'isearch-filter-predicate)
-                   #'wdired-isearch-filter-read-only)
+  (when wdired-search-replace-filenames
+    (remove-function (local 'isearch-search-fun-function)
+                     #'dired-isearch-search-filenames)
+    (kill-local-variable 'replace-search-function)
+    (kill-local-variable 'replace-re-search-function)
+    ;; Restore dired hook
+    (add-hook 'isearch-mode-hook #'dired-isearch-filenames-setup nil t))
   (use-local-map dired-mode-map)
   (force-mode-line-update)
   (setq buffer-read-only t)