(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)))
(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 ()
(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
(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)))
: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
(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 ()
(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)
;; 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 ()
(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)