3

In a previous question, Make region(s) invisible (not evaluated) to query-replacy and similar commands, I learned about isearch-filter-predicate. Now, I wonder if is there a similar method to make regions invisible to functions like string-match, occur etc.

For example I have this LaTeX code:

\begin{eqnarray}
\begin{matrix}
\newline
\end{matrix}
\end{eqnarray}

I use string-match-p to test if the \newline (\\) string occurs in the LaTeX eqnarray environment. If nil I replace "eqnarray" --> "equation". I also need to "ignore" newlines that occur in sub-enviroments like matrix, so I thought I could make the regions of these sub-enviroments "invisible" to the string-match-p function.

Is there a way to do it? Alternatively I thought I could write the isearch-filter-predicate combined with the search-forward-regexp function to test for the occurrence of my target string in the region.

This is the code I wrote:

(defun skip-if-my-ignore (beg end)
  "Return nil if some text BEG to END has non-`nil' property `my-ignore'."
  (catch 'skip-if-my-ignore
    (let ((pos  beg))
      (while (< pos end)
        (when (get-text-property pos 'my-ignore) (throw 'skip-if-my-ignore nil))
        (setq pos  (1+ pos))))
    t))

(defun test-invisibility ()
  (interactive)
  (save-excursion
    (let* ((a (copy-marker (point-min)))

       (SUB_ENVIRONMENTS (regexp-opt '(
                       "array"
                       "cases"
                       "pmatrix"
                       "matrix"
                       ))))

      (unwind-protect
      (progn
        (goto-char a)
        (while (search-forward-regexp (concat
                       "\\\\begin{"
                       "\\(" SUB_ENVIRONMENTS "\\)"
                       "}")
                      nil t)
          (save-excursion
        (let* ((b (copy-marker (match-beginning 0)))
               (SUB_ENV (match-string 1))
               (e (copy-marker (search-forward-regexp
                    (concat "\\\\end{" SUB_ENV "}") nil t))))

          (put-text-property b e 'my-ignore t)

          (save-restriction
            (narrow-to-region b e)

            (let ((isearch-filter-predicate  'skip-if-my-ignore))

              ;; should not ask for replacements (in sub-environments)
              (perform-replace "\\\\newline" "MATCHED" t t nil 1 nil b e) 

              (if (string-match-p "\\\\newline" (buffer-substring b e))
              (read-string "string-match-p matched the string. I don't want this!")

            (read-string "string-match-p didn't match the string. I WANT THIS!"))

              (if (search-forward-regexp "\\\\newline" e t)
              (read-string "search-forward-regexp matched the string. I don't want this!")

            (read-string "search-forward-regexp didn't match the string. I WANT THIS!"))

              ;; should not find occurrences in the sub-environments:
              (occur "\\\\newline" 1))))))))
    ;; *UNWINDFORMS
    (remove-list-of-text-properties (point-min) (point-max) '(my-ignore))))))
  • 1. You could simplify the code you present here, to make it easier. 2. Why would you expect string-match(-p) or occur to be sensitive to isearch-filter-predicate? They are not, nor are functions such as re-search-forward (used by occur-1). Isearch and perform-replace are sensitive to it. – Drew Feb 13 at 0:14
  • You can check for your text property during your scan of the buffer (your test...). Something like that is what you'll need to do. You could define a function that does only that and use that in other code. See next-single-char-property-change, for a start. – Drew Feb 13 at 0:18
  • @Drew I know I can use an alternative method to test for text properties on the region. I was only wondering if there was a system to hide some portions of the buffer to string-match and similar functions. This would make my job easier. – Gabriele Nicolardi Feb 13 at 7:02
  • Not with isearch-filter-predicate, to my knowledge anyway. But perhaps someone will post a relevant and helpful answer for you. – Drew Feb 13 at 16:28
  • You can modify the buffer temporarily for the execution of a function with cmdbufmod. You could look for an eqnarray environment, restrict the buffer to that region, delete the sub-environments in the entries of BUFMOD-LIST of cmdbufmod, run your command, and leave cmdbufmod restoring the deleted sub-environments. – Tobias Feb 15 at 14:45
0

I unexpectedly found that also how-many, search-forward, re-search-forward and their "backward" versions are not sensitive to isearch-filter-predicate. So I wrote a modified version of these functions that are sensitive to isearch-filter-predicate:

(defun search-forward-ifp (STRING &optional BOUND NOERROR COUNT)
  "Modified version of `search-forward' that filters (skips)
matches according to `isearch-filter-predicate'."

  (let ((POINT (point)))
    (catch 'filtered
      (while (search-forward STRING BOUND NOERROR COUNT)
        (let ((B (match-beginning 0))
              (E (match-end 0)))
          (when (funcall isearch-filter-predicate B E)
            (throw 'filtered (point)))))
      (goto-char POINT)
      nil)))

(defun search-backward-ifp (STRING &optional BOUND NOERROR COUNT)
  "Modified version of `search-backward' that filters (skips)
matches according to `isearch-filter-predicate'."

  (let ((POINT (point)))
    (catch 'filtered
      (while (search-backward STRING BOUND NOERROR COUNT)
        (let ((B (match-beginning 0))
              (E (match-end 0)))
          (when (funcall isearch-filter-predicate B E)
            (throw 'filtered (point)))))
      (goto-char POINT)
      nil)))

(defun re-search-forward-ifp (REGEXP &optional BOUND NOERROR COUNT)
  "Modified version of `search-forward-regexp' that
filters (skips) matches according to `isearch-filter-predicate'."

  (let ((POINT (point)))
    (catch 'filtered
      (while (search-forward-regexp REGEXP BOUND NOERROR COUNT)
        (let ((B (match-beginning 0))
              (E (match-end 0)))
          (when (funcall isearch-filter-predicate B E)
            (throw 'filtered (point)))))
      (goto-char POINT)
      nil)))
(defalias 'search-forward-regexp-ifp 're-search-forward-ifp)

(defun re-search-backward-ifp (REGEXP &optional BOUND NOERROR COUNT)
  "Modified version of `search-backward-regexp' that
filters (skips) matches according to `isearch-filter-predicate'."

  (let ((POINT (point)))
    (catch 'filtered
      (while (search-backward-regexp REGEXP BOUND NOERROR COUNT)
        (let ((B (match-beginning 0))
              (E (match-end 0)))
          (when (funcall isearch-filter-predicate B E)
            (throw 'filtered (point)))))
      (goto-char POINT)
      nil)))
(defalias 'search-backward-regexp-ifp 're-search-backward-ifp)

(defun how-many-ifp (regexp &optional rstart rend interactive)
  "Modified version of `how-many' that filters (skips) matches
according to `isearch-filter-predicate'."

  (interactive
   (keep-lines-read-args "How many matches for regexp"))
  (save-excursion
    (if rstart
        (if rend
            (progn
              (goto-char (min rstart rend))
              (setq rend (max rstart rend)))
          (goto-char rstart)
          (setq rend (point-max)))
      (if (and interactive (use-region-p))
          (setq rstart (region-beginning)
                rend (region-end))
        (setq rstart (point)
              rend (point-max)))
      (goto-char rstart))
    (let ((count 0)
          opoint
          (case-fold-search
           (if (and case-fold-search search-upper-case)
               (isearch-no-upper-case-p regexp t)
             case-fold-search)))
      (while (and (< (point) rend)
                  (progn (setq opoint (point))
                         (re-search-forward-ifp regexp rend t)))
        (if (= opoint (point))
            (forward-char 1)
          (setq count (1+ count))))
      (when interactive (message "%d occurrence%s"
                                 count
                                 (if (= count 1) "" "s")))
      count)))
(defalias 'count-matches-ifp 'how-many-ifp)

I think that something similar could be done with the occur function but I haven't tried it yet.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.