3

I have a .tex file that looks something like this:

$ cat foo.tex

\begin{document}

\begin{question}
lots of
multiline text

with several
paragraphs

of information
\end{question}

other text

\begin{question}
more text

here that i want to

manipulate

easily
\end{question}

\end{document}

I want to apply my-function to every region occuring between \begin{question} and \end{question}. Is this easily accomplished in emacs?

  • Can we assume that my-function accepts arguments for the beginning and end of the region? – phils Jun 13 at 21:16
1

Yes. See library Zones, in particular, function zz-do-zones or zz-map-zones.

From the Commentary in zones.el, section Define Your Own Commands:

Pretty much anything you can do with the Emacs region you can do with a set of zones (i.e., with a non-contiguous "region"). But existing Emacs commands that act on the region do not know about non-contiguous regions. What you will need to do is define new commands that take these into account.

You can define your own commands that iterate over a list of izones in a given buffer, or over such lists in a set of buffers. Utility functions zz-izone-limits, zz-izone-limits-in-bufs, and zz-read-bufs, zz-do-zones, zz-do-izones, zz-map-zones, and zz-map-izones can help with this.

As examples of such commands, if you use library highlight.el then you can use C-x n h (command hlt-highlight-regions) to highlight the izones recorded for the current buffer. You can use C-x n H (command hlt-highlight-regions-in-buffers) to do the same across a set of buffers that you specify (or across all visible buffers). If option hlt-auto-faces-flag is non-nil then each zone gets a different face. Otherwise, all of the zones are highlighted with the same face. Complementary (unbound) commands hlt-unhighlight-regions and hlt-unhighlight-regions-in-buffers unhighlight.

Defining your own command can be simple or somewhat complex, depending on how the region is used in the code for the corresponding region-action Emacs command. The definition of hlt-highlight-regions just calls existing function hlt-highlight-region once for each recorded zone:

(defun hlt-highlight-regions (&optional regions face msgp mousep buffers)
 "Apply `hlt-highlight-region' to regions in `zz-izones'."
 (interactive (list (zz-izone-limits zz-izones)
                    nil
                    t
                    current-prefix-arg))
 (dolist (start+end  regions)
   (hlt-highlight-region (nth 0 start+end) (nth 1 start+end)
                         face msgp mousep buffers)))

That's it - just iterate over zz-izones with a function that takes a zone as an argument. What zones.el offers in this regard is a way to easily define a set of buffer zones.

The doc also tells you how to easily create zones from bits of text such as what you describe. See, for example, function (command) zz-add-zone:

zz-add-zone is an interactive Lisp function in zones.el.

It is bound to C-x n a.

(zz-add-zone START END &optional VARIABLE NOT-BUF-LOCAL-P SET-VAR-P MSG)

Add an izone for the region to the izones of VARIABLE.

But do not add a zone if it would cover the entire buffer. Return the new value of VARIABLE.

This is a destructive operation: The list structure of the variable value can be modified.

VARIABLE defaults to the value ofzz-izones-var`.

With a prefix arg you are prompted for a different variable to use, in place of the current value of zz-izones-var. The particular prefix arg determines whether the variable, if unbound, is made buffer-local, and whether zz-izones-var is set to the variable symbol:

 prefix arg          buffer-local   set ‘zz-izones-var’
 ----------          ------------   -------------------
 Plain ‘C-u’         yes            yes
 > 0 (e.g. ‘C-1’)    yes            no
 = 0 (e.g. ‘C-0’)    no             yes
 < 0 (e.g. ‘C--’)    no             no

Non-interactively:

  • START and END are as for ‘narrow-to-region’.
  • VARIABLE is the optional izones variable to use.
  • Non-nil NOT-BUF-LOCAL-P means do not make VARIABLE buffer-local.
  • Non-nil SET-VAR-P means set zz-izones-var to VARIABLE.
  • Non-nil MSG means echo the zone limits, preceded by string MSG.

See also zz-add-region-as-izone which also adds the region as an izone, but which has a simpler signature and so can be used as a hook function. (In particular, it is used on deactivate-mark-hook by zz-auto-add-region-as-izone-mode, to automatically add the region as an izone each time it is deactivated.)

1

The easiest way I can think of is to use query-replace-regexp. That works if my-function takes the region as string and transforms it into the new string such as demonstrated with upcase in the following example.

BTW, I do not know how long the texts in the question environment may be.

Go to the beginning of the buffer and type M-C-% for query-replace-regexp and input the following FROM and TO strings:

\\begin{question}\(\(.\|^Q^J\)*?\)\\end{question}

where ^Q^J means pressing C-qC-j to insert a line break in the FROM string. Note the use of the non-greedy repetition operator *? to match until the next \end{question} (* would match until the last \end{question}).

\\begin{question}\,(upcase \1)\\end{question}

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.