I need to write some Emacs-Lisp code that uses the perform-replace function, limiting its range of action only on text (ignoring comments, in-line math and math ambients) on LaTeX documents (in latex-mode). I'd like to do that in native Emacs. Is there a way to do that without the need for external packages?

  • If this is a one-off and the document is relatively short, then I'd do it using an interactive search-and-replace, skipping the things that don't need changing. – NickD May 17 '17 at 15:40
  • Do you use AUCTeX? It has facilities to test whether point is in a comment or in math mode. – giordano May 17 '17 at 15:46
  • @giordano, sorry, I do not use AUCTeX (and I can't...). – Gabriele Nicolardi May 17 '17 at 16:18
  • @Nick I'm a professional typesetter so I'm looking for something more powerful of an interactive search-and-replace – Gabriele Nicolardi May 17 '17 at 16:18
  • OK, I don't understand the use-case: you have to do search-and-replace in a lot of documents? Why don't the authors do it right in the first place? Moreover, if you are not the author, why do you have to do it? – NickD May 17 '17 at 17:07

You ask whether it is possible without an external package. The answer is, "of course" ;-). Are you also asking someone to give you the code for that here (which is essentially giving you an external package ;-))? Maybe someone else will provide that info.

A short answer is that you can define isearch-filter-predicate so that Isearch and perform-replace skip matches that are in the areas you want to skip (e.g. comments and whatever else).

If you don't mind trying a 3rd-party package, consider Isearch+. Out of the box you can interactively skip searching comments, and you should be able to do the same for math expressions (whatever they are - you will need to define a suitable predicate). Then you can use the same filtering that you set up and tested using Isearch to perform replacements (e.g. with query-replace or whatever).

For example, during Isearch:

  1. C-z & prompts you for a filter predicate to add. You can pick a predefined predicate using completion. One predefined predicate is ~[;], which means search only outside comments. You do this: C-z & TAB, then choose candidate ~[;], or just do C-z & ~[;].

    The ~ in this predicate nickname stands for not, the [...] stands for inside, and the ; stands for comment (; is the comment character for Lisp, so I chose that to stand for "comment").

    You are now searching everywhere except in comments.

  2. To switch from Isearch to query-replacing, use M-%. Since you did this during Isearch the current isearch-filter-predicate carries over to replacement: You will be queried for matches only in non-comment zones.

What if you don't switch to query-replace during the same Isearch where you set up the predicate? When you finish isearching the predicate automatically goes back to what it was before you started using C-z.... But if you use C-z s before you exit Isearch then the predicate remains as you last defined it.

This means that after step #1 above, you could use C-z s, exit Isearch, do something else, and then later use query-replace. It will pick up that "saved" filter predicate.

(You can use C-z 0 during Isearch to remove your changes to the filter predicate. You can also toggle to search the complement of what the current filter would normally choose.)

You will need to define your own predicates for filtering math zones. The code in library isearch+.el for defining other filter predicates can help as a model.

The doc describing this on-the-fly defining and using filter predicates is at Dynamic Isearch Filtering.

  • Thank you. I'm not asking someone to give me the code ;-). I didn't know about isearch-filter-predicate so now I have my homework to do. I have to find a way to change that variable in my functions and restore the default value at the end. (I'm not properly a hacker... so this will be a little tricky for me.) Thank you again. – Gabriele Nicolardi May 17 '17 at 17:05
  • 1
    You can probably just let-bind it. When the let-binding is done the previous value is restored. – Drew May 17 '17 at 17:36
  • 1
    You can use face-at-point to recognize and skip comments and formulas (which will have their own faces, I suppose). – Eleno May 19 '17 at 0:01
  • @Elena: That seems like a comment for the question, not for this answer, no? – Drew May 19 '17 at 4:02
  • Elena is right with a comment complementary to the answer. face-at-point is useful when the buffer is already fontified, and when not then better would be to rely on syntax, e.g. (and comment-start (nth 4 (syntax-ppss))). – link0ff May 22 '17 at 20:30

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.