diff --git a/lisp/isearch.el b/lisp/isearch.el index 81e83d7950..e87f7f6756 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -145,6 +145,9 @@ search-invisible has a non-nil property `isearch-open-invisible', then incremental search will show the hidden text. (This applies when using `outline.el' and `hideshow.el'.) +When the value is `open-all', consider text made invisible by text +property as well. Text property `isearch-open-invisible' will be +considered in the same way as for overlays. To temporarily change the value for an active incremental search, use \\\\[isearch-toggle-invisible]. @@ -942,6 +945,7 @@ isearch-new-forward ;; Accumulate here the overlays opened during searching. (defvar isearch-opened-overlays nil) +(defvar isearch-opened-regions nil) ;; Non-nil if the string exists but is invisible. (defvar isearch-hidden nil) @@ -1218,6 +1222,7 @@ isearch-mode isearch-opoint (point) search-ring-yank-pointer nil isearch-opened-overlays nil + isearch-opened-regions nil isearch-input-method-function input-method-function isearch-input-method-local-p (local-variable-p 'input-method-function) regexp-search-ring-yank-pointer nil @@ -1551,6 +1556,7 @@ isearch-exit (let ((isearch-nonincremental t)) (isearch-edit-string)) ;; this calls isearch-done as well (isearch-done)) + (isearch-clean-regions) (isearch-clean-overlays)) (defun isearch-fail-pos (&optional msg) @@ -1807,6 +1813,7 @@ isearch-cancel (isearch--set-state (car isearch-cmds))) (goto-char isearch-opoint)) (isearch-done t) ; Exit isearch.. + (isearch-clean-regions) (isearch-clean-overlays) (signal 'quit nil)) ; ..and pass on quit signal. @@ -2271,6 +2278,7 @@ isearch-query-replace (isearch-string-propertized (isearch-string-propertize isearch-string))) (isearch-done nil t) + (isearch-clean-regions) (isearch-clean-overlays) (if (and isearch-other-end (if backward @@ -2376,6 +2384,7 @@ isearch--highlight-regexp-or-lines ;; We call `exit-recursive-edit' explicitly at the end below. (isearch-recursive-edit nil)) (isearch-done nil t) + (isearch-clean-regions) (isearch-clean-overlays)) (require 'hi-lock nil t) (let ((regexp (cond ((functionp isearch-regexp-function) @@ -2958,6 +2967,7 @@ isearch-pre-command-hook ;; Other characters terminate the search and are then executed normally. (search-exit-option (isearch-done) + (isearch-clean-regions) (isearch-clean-overlays))))) (defun isearch-post-command-hook () @@ -3312,6 +3322,22 @@ isearch-search-fun-function This returned function will be used by `isearch-search-string' to search for the first occurrence of STRING.") +(defun isearch--find-text-property-region (pos prop) + "Find a region around POS containing PROP text property. +Return nil when PROP is not set at POS." + (let* ((beg (and (get-text-property pos prop) pos)) + (end beg)) + (when beg + (unless (or (equal beg (point-min)) + (not (eq (get-text-property beg prop) + (get-text-property (1- beg) prop)))) + (setq beg (previous-single-property-change pos prop nil (point-min)))) + (unless (or (equal end (point-max)) + (not (eq (get-text-property end prop) + (get-text-property (1+ end) prop)))) + (setq end (next-single-property-change pos prop nil (point-max)))) + (cons beg end)))) + (defun isearch-search-fun () "Return the function to use for the search. Can be changed via `isearch-search-fun-function' for special needs." @@ -3421,8 +3447,9 @@ isearch-search (if (or (not isearch-success) (bobp) (eobp) (= (match-beginning 0) (match-end 0)) - (funcall isearch-filter-predicate - (match-beginning 0) (match-end 0))) + (save-match-data + (funcall isearch-filter-predicate + (match-beginning 0) (match-end 0)))) (setq retry nil))) (setq isearch-just-started nil) (if isearch-success @@ -3481,6 +3508,20 @@ isearch-open-overlay-temporary (overlay-put ov 'isearch-invisible (overlay-get ov 'invisible)) (overlay-put ov 'invisible nil))) +;; Same as `isearch-open-overlay-temporary', but for text hidden via text +;; properties. +(defun isearch-open-region-temporary (hidden-region) + (if (not (null (get-text-property (car hidden-region) 'isearch-open-invisible-temporary))) + (funcall (get-text-property (car hidden-region) 'isearch-open-invisible-temporary) hidden-region nil) + (with-silent-modifications + (put-text-property (car hidden-region) + (cdr hidden-region) + 'isearch-invisible + (get-text-property (point) 'invisible)) + (put-text-property (car hidden-region) + (cdr hidden-region) + 'invisible + nil)))) ;; This is called at the end of isearch. It will open the overlays ;; that contain the latest match. Obviously in case of a C-g the @@ -3501,6 +3542,23 @@ isearch-open-necessary-overlays (if fct-temp (funcall fct-temp ov t))))) +;; Equivalent of `isearch-open-necessary-overlays' for text hidden via text +;; properties. +(defun isearch-open-necessary-region (region) + (let ((inside-region (and (> (point) (car region)) + (<= (point) (cdr region)))) + (fct-temp (get-text-property (car region) 'isearch-open-invisible-temporary))) + (when (or inside-region (not fct-temp)) + (with-silent-modifications + (put-text-property (car region) + (cdr region) + 'invisible + (get-text-property (car region) 'isearch-invisible)) + (remove-text-properties (car region) (cdr region) '('isearch-invisible nil)))) + (if inside-region + (funcall (get-text-property (car region) 'isearch-open-invisible) region) + (when fct-temp (funcall fct-temp region t))))) + ;; This is called when exiting isearch. It closes the temporary ;; opened overlays, except the ones that contain the latest match. (defun isearch-clean-overlays () @@ -3508,6 +3566,10 @@ isearch-clean-overlays (mapc 'isearch-open-necessary-overlays isearch-opened-overlays) (setq isearch-opened-overlays nil))) +(defun isearch-clean-regions () + (when isearch-opened-regions + (mapc 'isearch-open-necessary-region isearch-opened-regions) + (setq isearch-opened-regions nil))) (defun isearch-intersects-p (start0 end0 start1 end1) "Return t if regions START0..END0 and START1..END1 intersect." @@ -3535,6 +3597,21 @@ isearch-close-unnecessary-overlays (overlay-put ov 'invisible (overlay-get ov 'isearch-invisible)) (overlay-put ov 'isearch-invisible nil))))))) +(defun isearch-close-unnecessary-regions (begin end) + (let ((regions isearch-opened-regions)) + (setq isearch-opened-regions nil) + (dolist (region regions) + (if (isearch-intersects-p begin end (car region) (cdr region)) + (push region isearch-opened-regions) + (let ((fct-temp (get-text-property (car region) 'isearch-open-invisible-temporary))) + (if fct-temp + (funcall fct-temp region t) + (with-silent-modifications + (put-text-property (car region) + (cdr region) + 'invisible + (get-text-property (car region) 'isearch-invisible)) + (remove-text-properties (car region) (cdr region) '('isearch-invisible nil))))))))) (defun isearch-range-invisible (beg end) "Return t if all the text from BEG to END is invisible." @@ -3542,52 +3619,70 @@ isearch-range-invisible ;; Check that invisibility runs up to END. (save-excursion (goto-char beg) - (let (;; can-be-opened keeps track if we can open some overlays. - (can-be-opened (eq search-invisible 'open)) + (let (;; can-be-opened keeps track if we can open some hidden text. + (can-be-opened (memq search-invisible '(open open-all))) + (region-can-be-opened (eq search-invisible 'open-all)) ;; the list of overlays that could be opened - (crt-overlays nil)) + (crt-overlays nil) + (crt-regions nil)) (when (and can-be-opened isearch-hide-immediately) - (isearch-close-unnecessary-overlays beg end)) + (isearch-close-unnecessary-overlays beg end) + (when region-can-be-opened (isearch-close-unnecessary-regions beg end))) ;; If the following character is currently invisible, ;; skip all characters with that same `invisible' property value. ;; Do that over and over. (while (and (< (point) end) (invisible-p (point))) - (if (invisible-p (get-text-property (point) 'invisible)) - (progn - (goto-char (next-single-property-change (point) 'invisible - nil end)) - ;; if text is hidden by an `invisible' text property - ;; we cannot open it at all. - (setq can-be-opened nil)) - (when can-be-opened - (let ((overlays (overlays-at (point))) - ov-list - o - invis-prop) - (while overlays - (setq o (car overlays) - invis-prop (overlay-get o 'invisible)) - (if (invisible-p invis-prop) - (if (overlay-get o 'isearch-open-invisible) - (setq ov-list (cons o ov-list)) - ;; We found one overlay that cannot be - ;; opened, that means the whole chunk - ;; cannot be opened. - (setq can-be-opened nil))) - (setq overlays (cdr overlays))) - (if can-be-opened - ;; It makes sense to append to the open - ;; overlays list only if we know that this is - ;; t. - (setq crt-overlays (append ov-list crt-overlays))))) - (goto-char (next-overlay-change (point))))) - ;; See if invisibility reaches up thru END. - (if (>= (point) end) - (if (and can-be-opened (consp crt-overlays)) - (progn + (when (invisible-p (get-text-property (point) 'invisible)) + (if (and can-be-opened region-can-be-opened) + (let ((region (isearch--find-text-property-region (point) 'invisible))) + ;; If the region info is passed to user-defined + ;; function, tell that function what we actually + ;; want to reveal. + (when (< (car region) beg) (setcar region beg)) + (when (> (cdr region) end) (setcdr region end)) + (if (get-text-property (point) 'isearch-open-invisible) + (push region crt-regions) + (goto-char (next-single-property-change (point) 'invisible + nil end)) + (setq can-be-opened nil))) + (goto-char (next-single-property-change (point) 'invisible + nil end)) + ;; if text is hidden by an `invisible' text property + ;; we cannot open it at all. + (setq can-be-opened nil))) + (when can-be-opened + (let ((overlays (overlays-at (point))) + ov-list + o + invis-prop) + (while overlays + (setq o (car overlays) + invis-prop (overlay-get o 'invisible)) + (if (invisible-p invis-prop) + (if (overlay-get o 'isearch-open-invisible) + (setq ov-list (cons o ov-list)) + ;; We found one overlay that cannot be + ;; opened, that means the whole chunk + ;; cannot be opened. + (setq can-be-opened nil))) + (setq overlays (cdr overlays))) + (if can-be-opened + ;; It makes sense to append to the open + ;; overlays list only if we know that this is + ;; t. + (setq crt-overlays (append ov-list crt-overlays))))) + (goto-char (next-single-char-property-change (point) 'invisible))) + ;; See if invisibility reaches up thru END. + (if (>= (point) end) + (if (and can-be-opened (or (consp crt-overlays) + (consp crt-regions))) + (progn (setq isearch-opened-overlays - (append isearch-opened-overlays crt-overlays)) + (append isearch-opened-overlays crt-overlays)) (mapc 'isearch-open-overlay-temporary crt-overlays) + (setq isearch-opened-regions + (append isearch-opened-regions crt-regions)) + (mapc 'isearch-open-region-temporary crt-regions) nil) (setq isearch-hidden t)))))))