This page is about the use of library Zones, zones.el, to browse among multiple buffer restrictions (narrowings).

(Library zones.el supersedes library wide-n.el, which is obsolete as of 2015-08-16.

Problem: Narrowing Is Fine-Grained, But Widening Is Not

Did you ever narrow a buffer to some restriction (region), and then later narrow it some more? Ever want to then widen back to the first (larger) restriction, rather than just widening to the whole buffer? You cannot.

Narrowing always narrows to the current region, which can be anywhere and have any size up to the buffer size. But command ‘widen’ (‘C-x n w’) always restores the full buffer. There can be many different narrowings (one at a time), but there is only one widening: the whole buffer.

Library Zones gives you the same fine-grained behavior for widening as for narrowing. In fact, the possible widening targets are the same restrictions that you created by previous narrowings (plus the whole buffer). Instead of only a single widening, you have N of them.

A Ring of Narrowings

Library Zones advises ‘narrow-to-region’, the basic narrowing command, so that each time you narrow a buffer interactively the buffer restriction is recorded in a ring (which is stored in buffer-local variable ‘zz-izones’, by default). You can return to any of these narrowings selectively at any time.

‘C-x n x’ is bound to command ‘zz-narrow-repeat’, which restores the last recorded narrowing for the current buffer.

The narrowings can be nested, but they need not be. They can be any regions at all.

The mode-line lighter ‘Narrow’ is still used for the ordinary Emacs narrowing commands. But for ‘zz-narrow-repeat’ (‘C-x n x’) the current restriction is indicated in the lighter by a identifying number: ‘Narrow-1’, ‘Narrow-2’, and so on. ‘mouse-2’ on the ‘Narrow’ part still widens completely, but ‘mouse-2’ on the ‘-NUM’ part uses ‘zz-narrow-repeat’ to cycle to the next restriction.

If option ‘zz-narrowing-use-fringe-flag’ is non-nil, which it is by default, then the face of the selected frame’s fringe is set to ‘zz-fringe-for-narrowing’ whenever the buffer is narrowed. This shows you that the current buffer is narrowed even if the mode-line does not.

Emacs markers are used to record narrowing limits, so the same buffer restriction is available even if you modify its context. If for any reason ‘zz-izones’ ever has any entries that use buffer positions (numbers) instead of markers, invoking a zone command corrects this by changing the positions to markers.

You can serialize ‘zz-izones’ to use readable markers, save the value persistently, and restore it later. Library Bookmark+’ (bookmark+.el) does this in order to let you bookmark and restore a list of narrowings.

In normal use, only the interactive use of standard commands ‘narrow-to-region’, ‘narrow-to-defun’, and ‘narrow-to-page’ is affected by library zones.el. When these functions are called non-interactively there is normally no change to the value of variable ‘zz-izones’. However, if for some reason you want to add entries to the restrictions ring when narrowing with some EmacsLisp code (i.e. non-interactively), you can do so by binding variable ‘zz-izone-add-anyway-p’ around the narrowing call.

You can use ‘C-x n C-d’ (command ‘zz-izone-delete’) to delete a narrowing, giving its numeric identifier.

You can also add the current region to ‘zz-izones’ without first narrowing to it, using ‘C-x n s’ (command ‘zz-izone-add’). You need not activate the region to do this.

Restrictions Variables

By default, buffer-local variable ‘zz-izones’ holds the restrictions for the current buffer. But the functions provided by zones.el can use any variable that holds zones. And you can choose whether any such variable is buffer-local or not. If it is not then it can hold zones from multiple buffers.

Several commands defined in zones.el let you specify the zones variable to use, by prompting you if you use a prefix argument. For example, ‘C-u C-x n s’ (‘zz-izone-add’) prompts you for a zones variable, and ‘C-- C-x n s’ does the same but it does not make the variable buffer-local. You can specify any variable name, even if the variable does not yet exist (in which case it is given an empty value).

Other Uses of Buffer Zones, Besides Narrowing

Moving among different buffer restrictions, i.e., narrowing the buffer to different zones, is one use of library zones.el, but another important use case is performing actions on a set of buffer zones, including perhaps zones from different buffers.

‘C-x n r’ (command ‘zz-select-region-repeat’) does this, performing the action of selecting a zone in ‘zz-izones’ as the region. Repeat to cycle among the zones: `C-x n r r r...’.

You can define your own commands that iterate over the buffers and then over the entries in ‘zz-izones’ (or some subset of them) for each buffer. Utility functions ‘zz-izone-limits’, ‘zz-izone-limits-in-bufs’, and ‘zz-read-bufs’ can help with this.

As examples of such commands, if you use the Highlight library (highlight.el) then you can use ‘C-x n h’ (command ‘hlt-highlight-regions’) to highlight the zones recorded for the current buffer. And 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 region gets a different face. Otherwise, all of the regions are highlighted with the same face. Complementary (unbound) commands ‘hlt-unhighlight-regions’ and ‘hlt-unhighlight-regions-in-buffers’ unhighlight.

Another way to look at the possibility of acting on multiple buffer zones is to think of it as enlarging the notion of a “region” of text that you can operate on. In effect, it can remove the requirement of target text being a contiguous sequence of characters. A set of buffer zones is, in effect, a (typically) noncontiguous “region” of text.

You can use commands ‘zz-izones-coalesce’ and ‘zz-izone-add-and-coalesce’ to coalesce overlapping and contiguous zones, uniting them.

Pretty much anything you can do with the Emacs region you can do with a set of buffer zones (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.

This 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 region:

    (defun hlt-highlight-regions (&optional regions face msgp mousep buffers)
      "Apply `hlt-highlight-region' to each region in `zz-izones'."
      (interactive (list (zz-izone-limits) 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 the region as an argument. What zones.el offers in this regard is a way to easily define a set of buffer restrictions.

Enjoy! – DrewAdams

CategoryRegion Narrowing CategoryHideStuff