2

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?

10
  • 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
    Commented May 17, 2017 at 15:40
  • Do you use AUCTeX? It has facilities to test whether point is in a comment or in math mode.
    – giordano
    Commented May 17, 2017 at 15:46
  • @giordano, sorry, I do not use AUCTeX (and I can't...).
    – Gabriele
    Commented May 17, 2017 at 16:18
  • @Nick I'm a professional typesetter so I'm looking for something more powerful of an interactive search-and-replace
    – Gabriele
    Commented May 17, 2017 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
    Commented May 17, 2017 at 17:07

1 Answer 1

2

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.

8
  • 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
    Commented May 17, 2017 at 17:05
  • 1
    You can probably just let-bind it. When the let-binding is done the previous value is restored.
    – Drew
    Commented May 17, 2017 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
    Commented May 19, 2017 at 0:01
  • @Elena: That seems like a comment for the question, not for this answer, no?
    – Drew
    Commented May 19, 2017 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
    Commented May 22, 2017 at 20:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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