Zones of text - like multiple regions. Bug reports etc.: (concat "drew" ".adams" "@" "oracle" ".com") You can get `zones.el' from Emacs Wiki or GNU ELPA: * Emacs Wiki: https://www.emacswiki.org/emacs/download/zones.el * GNU ELPA: https://elpa.gnu.org/packages/zones.html The instance on Emacs Wiki might sometimes be more recent, but major changes (named ''versions'') are posted to GNU ELPA. More description below. (@> "Index") Index ----- If you have library `linkd.el', load `linkd.el' and turn on `linkd-mode' now. It lets you easily navigate around the sections of this doc. Linkd mode will highlight this Index, as well as the cross-references and section headings throughout this file. You can get `linkd.el' here: https://www.emacswiki.org/emacs/download/linkd.el. (@> "Things Defined Here") (@> "Documentation") (@> "Compatibility") (@> "Zones") (@> "Coalesced (United) Zones") (@> "Noncontiguous Region and Set of Zones") (@> "Zones and Overlays") (@> "Izone Commands") (@> "Izone List Variables") (@> "Keys") (@> "Command `zz-narrow-repeat'") (@> "Define Your Own Commands") (@> "Automatically Create Zones on Region Deactivation") (@> "Change Log") (@> "Compatibility Code for Older Emacs Versions") (@> "Variables and Faces") (@> "Advice for Standard Functions") (@> "General Commands") (@> "General Non-Interactive Functions") (@> "Key Bindings") (@* "Things Defined Here") Things Defined Here ------------------- Commands defined here: `zz-add-region-as-izone', `zz-add-zone', `zz-add-zone-and-coalesce', `zz-add-zone-and-unite', `zz-add-zones-from-highlighting', `zz-add-zones-matching-regexp', `zz-auto-add-region-as-izone-mode', `zz-clone-and-coalesce-zones', `zz-clone-and-unite-zones', `zz-clone-zones', `zz-coalesce-zones', `zz-delete-zone', `zz-narrow', `zz-narrow-repeat', `zz-query-replace-zones' (Emacs 25+), `zz-query-replace-regexp-zones' (Emacs 25+), `zz-select-region', `zz-select-region-by-id-and-text', `zz-select-region-repeat', `zz-select-zone', `zz-select-zone-by-id-and-text', `zz-select-zone-repeat', `zz-set-izones-var', `zz-set-zones-from-face', `zz-set-zones-from-highlighting', `zz-set-zones-matching-regexp', `zz-unite-zones'. User options defined here: `zz-auto-remove-empty-izones-flag', `zz-narrowing-adds-zone-flag', `zz-narrowing-use-fringe-flag' (Emacs 23+). Faces defined here: `zz-fringe-for-narrowing'. Non-interactive functions defined here: `zz-add-key-bindings-to-narrow-map', `zz-basic-zones', `zz-basic-zones-in-bufs', `zz-buffer-narrowed-p' (Emacs 22-23), `zz-buffer-of-markers', `zz-car-<', `zz-choose-zone-by-id-and-text', `zz-do-izones', `zz-dotted-zones-from-izones', `zz-do-zones', `zz-dot-pairs', `zz-empty-zone-p', `zz-every', `zz-get-overlay-props', `zz-izone-has-other-buffer-marker-p', `zz-izones-from-noncontiguous-region' (Emacs 25+), `zz-izones-from-zones', `zz-izone-p', `zz-izones-p', `zz-izones-renumber', `zz-map-izones', `zz-map-zones', `zz-marker-from-object', `zz-markerize', `zz-max', `zz-min', `zz-narrow-advice', `zz-narrowing-lighter', `zz-noncontiguous-region-from-izones', `zz-noncontiguous-region-from-zones', `zz-numberize', `zz-number-or-marker-p', `zz-numeric-position', `zz-order-zones', `zz-overlays-to-zones', `zz-overlay-to-zone', `zz-overlay-union', `zz-position-from-object', `zz-rassoc-delete-all', `zz-readable-marker', `zz-readable-markerize', `zz-readable-marker-p', `zz-read-any-variable', `zz-read-bufs', `zz-regexp-car-member', `zz-remove-empty-izones', `zz-remove-if', `zz-remove-if-not', `zz-remove-izones-w-other-buffer-markers', `zz-remove-zones-w-other-buffer-markers', `zz-repeat-command', `zz-same-position-p', `zz-set-intersection', `zz-set-union', `zz-some', `zz-string-match-p', `zz-two-zone-intersection', `zz-two-zone-union', `zz-zone-abstract-function-default', `zz-zone-buffer-name', `zz-zone-has-other-buffer-marker-p', `zz-zone-intersection', `zz-zone-intersection-1', `zz-zone-ordered', `zz-zones-complement', `zz-zones-from-noncontiguous-region' (Emacs 25+), `zz-zones-overlap-p', `zz-zones-same-buffer-name-p', `zz-zones-to-overlays', `zz-zone-to-overlay', `zz-zone-union', `zz-zone-union-1'. Internal variables defined here: `zz--fringe-remapping', `zz-add-zone-anyway-p', `zz-izones', `zz-izones-var', `zz-lighter-narrowing-part', `zz-zone-abstract-function', `zz-zone-abstract-limit', `zz-toggles-map'. Macros defined here: `zz-user-error'. ***** NOTE: These EMACS PRIMITIVES have been ADVISED HERE: `narrow-to-defun', `narrow-to-page', `narrow-to-region'. (@* "Documentation") Documentation ------------- Library `zones.el' lets you easily define and subsequently act on multiple zones of buffer text. You can think of this as enlarging the notion of "region". 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. (@* "Compatibility") ** Compatibility ** Some of the functions defined here are not available for Emacs versions prior to 23. Still others are available only starting with Emacs 25. This is mentioned where applicable. (@* "Zones") ** Zones ** A "zone" is a basic zone or an izone. A zone represents the text between its two positions, just as an Emacs region is the text between point and mark. A "basic zone" is a list of two buffer positions followed by a possibly empty list of extra information: (POS1 POS2 . EXTRA). An "izone" is a list whose first element is an identifier, ID, which is a negative integer (-1, -2, -3,...), and whose cdr is a basic zone. So an izone has the form (ID POS1 POS2 . EXTRA). The ID is negative just to distinguish a basic zone whose EXTRA list starts with an integer from an izone. Interactively (e.g. in prompts), references to the ID typically leave off the minus sign. The positions of a zone can be positive integers (1, 2, 3,...), markers for the same buffer, or readable markers for the same buffer. (Behavior is undefined if a single zone has markers for different buffers.) Each position of a given zone can take any of these forms. A "readable marker" is a list (marker BUFFER POSITION), where BUFFER is a buffer name (string) and where POSITION is a buffer position (as an integer, not as a marker). The content of a zone is any contiguous stretch of buffer text. The positions of a zone can be in either numeric order. The positions are also called the zone "limits". The lower limit is called the zone "beginning"; the upper limit is called its "end". (@* "Coalesced (United) Zones") ** Coalesced (United) Zones ** A list of zones can include zones that overlap or are adjacent (the end of one is one less than the beginning of the other). Basic-zone union and intersection operations (`zz-zone-union', `zz-zone-intersection') each act on a list of zones, returning another such list, but with the recorded positions for each zone in (ascending) buffer order, and with the zones in ascending order of their cars. For basic-zone union, the resulting zones are said to be "coalesced", or "united". The extra info in the zones that result from zone union or intersection is just the set union or set intersection of the extra info in the zones that are combined. After a list of zones has been altered by `zz-zone-union' or `zz-zone-intersection': * Each zone in the result list is ordered so that its first element is smaller than its second. * The zones in the result list have been sorted in ascending order by their first elements. * The zones in the result list are disjoint: they are not adjacent and do not overlap: there is some other buffer text (i.e., not in any zone) between any two zones in the result. (@* "Noncontiguous Region and Set of Zones") ** Noncontiguous Region and Set of Zones ** Starting with Emacs 25, Emacs can sometimes use a region that is made up of noncontiguous pieces of buffer content: a "noncontiguous region". This is similar to a set of zones, but there are some differences. The zones in a set (or list) of zones can be adjacent or overlap, and their order in the set is typically not important. A noncontiguous region corresponds instead to what results from coalescing (uniting) a set of zones: a sequence of disjoint zones, in buffer order, that is, ordered by their cars. The Lisp representation of a zone also differs from that of a segment of a noncontiguous region. Each records two buffer positions, but a zone can also include a list of additional information (whatever you like). A noncontiguous-region segment is a cons (BEGIN . END), with BEGIN <= END. A zone is a list (LIMIT1 LIMIT2 . EXTRA) of two positions optionally followed by a list of extra stuff (any Lisp objects). And as stated above, the zone limits need not be in ascending order. The last difference is that each buffer position of a zone can be a marker, which means that a list of zones can specify zones in different buffers. A zone position can also be a readable marker, which is a Lisp sexp that can be written to disk (e.g., as part of a bookmark or saved variable), and restored in a later Emacs session by reading the file where it is saved. (@* "Zones and Overlays") ** Zones and Overlays ** Zones have even more in common with Emacs overlays than they do with segments of a noncontiguous region. An overlay has an associated buffer, two limits (start and end), and an optional list of properties. Zones differ from overlays in these ways: * A zone can have an identifier (izone). * A zone can have a readable Lisp form, by using numbers or readable markers. * A zone need not be specific to a particular buffer. If a zone's positions are numbers instead of markers then you can use it in any buffer. * A set of zones can be persistent, by bookmarking it. You can create zones from overlays, and vice versa, using functions `zz-overlay-to-zone', `zz-zone-to-overlay', `zz-overlays-to-zones', and `zz-zones-to-overlays'. When creating zones from overlays you can specify how to represent the zone limits: using markers, readable markers, or positive integers. And you can specify whether to create basic zones or izones. The resulting zone has ((:zz-overlay . PROPS)) as its list of extra information, where PROPS is the overlay's property list. So the zone is (LIMIT1 LIMIT2 (:zz-overlay . PROPS)) (or the same with an identifier, if an izone). When creating overlays from zones, the presence of a list (:zz-overlay . PROPS) in the extra zone information results in the overlay having PROPS as its property list. When creating a single such overlay you can optionally specify additional overlay properties, as well as arguments FRONT-ADVANCE and REAR-ADVANCE for function `make-overlay'. You can use function `zz-overlay-union' to coalesce overlays in a given buffer that overlap or are adjacent. (@* "Izone Commands") ** Izone Commands ** Commands that manipulate lists of zones generally use izones, because they make use of the zone identifiers. Things you can do with zones: * Sort them. * Unite (coalesce) adjacent or overlapping zones (which includes sorting them in ascending order of their cars). * Intersect them. * Narrow the buffer to any of them. Cycle among narrowings. If you use library `icicles.el' then you can also navigate among them in any order, and using completion against BEG-END range names. * Select any of them as the active region. Cycle among them. * Search them (they are automatically coalesced first). For this you need library `isearch-prop.el'. * Make a set of zones or its complement (the anti-zones) invisible. For this you also need library `isearch-prop.el'. * Highlight and unhighlight them. For this you need library `highlight.el' or library `facemenu+.el' (different kinds of highlighting). * Add zones of highlighted text (overlay or text-property highlighting). For this you need library `highlight.el'. (You can highlight many ways, including dragging the mouse.) * Add the active region to a list of zones. * Add the active region to a list of zones, and then unite (coalesce) the zones. * Delete an izone from a list of zones. * Clone a zones variable to another one, so the clone has the same zones. * Clone a zones variable and then unite the zones of the clone. * Make an izone variable persistent, in a bookmark. Use the bookmark to restore it in a subsequent Emacs session. For this you need library `bookmark+.el'. * Query-replace over them (Emacs 25 and later). (@* "Izone List Variables") ** Izone List Variables ** Commands that use izones generally use a variable that holds a list of them. By default, this is the buffer-local variable `zz-izones'. But such a variable can be buffer-local or global. If it is global then it can use markers and readable markers for different buffers. The value of variable `zz-izones-var' is the variable currently being used by default for izone commands. The default value is `zz-izones'. You can have any number of izones variables, and they can be buffer-local or global variables. You can use `C-x n v' (command `zz-set-izones-var') anytime to set `zz-izones-var' to a variable whose name you enter. With a prefix argument, the variable is made automatically buffer-local. Use `C-x n v' to switch among various zone variables for the current buffer (if buffer-local) or globally. Sometimes another zone command prompts you for the izones variable to use, if you give it a prefix argument. The particular prefix arg determines whether the variable, if not yet bound, 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 For example, `C-u C-x n a' (`zz-add-zone') prompts you for a different variable to use, in place of the current value of `zz-izones-var'. The variable you enter is made buffer-local and it becomes the new default izones variable for the buffer; that is, `zz-izones-var' is set to the variable symbol. As another example, suppose that `zz-izones-var' is `zz-izones', the default value and buffer-local by design. If you then use `C-- C-x n a' and enter a variable name at the prompt, that variable is not made buffer-local, and `zz-izones-var' is not set to that variable. The active region is pushed to the variable, but because `zz-izones-var' is unchanged, a subsequent `C-x n a' (no prefix arg) pushes to `zz-izones'. (@* "Keys") ** Keys ** Many of the commands that manipulate izones are bound on keymap `narrow-map'. So they are available on prefix key `C-x n' (by default), along with the narrowing/widening keys `C-x n d', `C-x n n', `C-x n p', and `C-x n w'. (If you use Emacs 22 then there is no `narrow-map', so the same `n ...' keys are bound on keymap `ctl-x-map'.) If you have already bound one of these keys then `zones.el' does not rebind that key; your bindings are respected. C-x n # `zz-select-zone-by-id-and-text' - Select zone as region C-x n M-% `zz-query-replace-zones' - Query-replace within zones C-x n C-M-% - Regexp query-replace C-x n M-= ~ `isearchp-toggle-complementing-domain' - Toggle searching anti-zones C-x n M-= d `isearchp-toggle-dimming-outside-search-area' - Toggle dimming text that is outside the search area C-x n M-= v `isearchp-toggle-anti-zones-invisible' - Toggle visibility of the complement of (the union of) zones C-x n M-= V `isearchp-toggle-zones-invisible' - Toggle visibility of zones C-x n a `zz-add-zone' - Add to current izones set (variable) C-x n A `zz-add-zone-and-unite' - Add zone, then unite zones C-x n c `zz-clone-zones' - Clone zones from one var to another C-x n C `zz-clone-and-unite-zones' - Clone then unite zones C-x n d `narrow-to-defun' C-x n D `isearchp-remove-dimming' - Remove text dimming C-x n C-d `zz-delete-zone' - Delete an izone from current var C-x n f `zz-set-zones-from-face' - Set zone set to face areas C-x n h `hlt-highlight-regions' - Ad hoc zone highlighting C-x n H `hlt-highlight-regions-in-buffers' - in multiple buffers C-x n l `zz-add-zones-from-highlighting' - Add from highlighted C-x n L `zz-set-zones-from-highlighting' - Set to highlighted C-x n n `narrow-to-region' C-x n p `narrow-to-page' C-x n P `isearchp-put-prop-on-zones' - Add text property C-x n r `zz-add-zones-matching-regexp' - Add regexp-match zones C-x n R `zz-set-zones-matching-regexp' - Set zone set to matches C-x n C-r `isearchp-zones-backward' - Search backward within zones C-x n C-M-r - Regexp-search backward C-x n s `zz-select-zone-repeat' - Cycle zones as active region (negative arg removes zone) C-x n C-s `isearchp-zones-forward' - Search forward within zones C-x n C-M-s - Regexp-search forward C-x n u `zz-unite-zones' - Unite (coalesce) zones C-x n v `zz-set-izones-var' - Set current zones-set variable C-x n w `widen' C-x n x `zz-narrow-repeat' - Cycle or pop zones as narrowings For the `hlt*' commands you need library `highlight.el': `C-x n h', `C-x n H'. For the `isearchp-' commands you need library `isearch-prop.el': `C-x n M-= ~', `C-x n M-= d', `C-x n M-= v', `C-x n M-= V', `C-x n D', `C-x n P', `C-x n C-r', `C-x n C-M-r', `C-x n C-s', 'C-x n C-M-s`. (@* "Command `zz-narrow-repeat'") ** Command `zz-narrow-repeat' ** Library `zones.el' modifies commands `narrow-to-region', `narrow-to-defun', and `narrow-to-page' (`C-x n n', `C-x n d', and `C-x n p') so that the current buffer restriction (narrowing) is added to the izone list of the current buffer (by default, buffer-local variable `zz-izones'). You can then use `C-x n x' to cycle or pop previous narrowings. Repeating `x' repeats the action: `C-x n x x x x' etc. Each time you hit `x' a different narrowing is made current. This gives you an easy way to browse your past narrowings. If the izone variable is not buffer-local then `zz-narrow-repeat' can cycle among the narrowings in different buffers, switching the buffer accordingly. Invoking `C-x n x' with a prefix argument changes the behavior as follows: * A plain prefix arg (`C-u') widens the buffer completely. * A zero numeric prefix arg (e.g `C-0') widens completely and resets (empties) the current izone variable. * A numeric prefix arg N takes you directly to the abs(N)th previous buffer narrowing. That is, it acts abs(N) times. A negative arg work like a positive one, except that it also pops entries off the ring: it removes entries from the most recent back through the (-)Nth one. For example, `C-- C-x n x x x' pops the last added narrowing each time you hit `x'. You can thus use the list of recorded zones as a narrowing stack: narrow commands push to the stack, and `C-- C-x n x' pops it. By default, `C-x n x' is bound to command `zz-narrow-repeat'. (For Emacs versions prior to 22 it is bound by default to `zz-narrow', which is a non-repeatable version. Repeatability is not available before Emacs 22.) 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 narrowing is indicated in the lighter by an 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 narrowing. If option `zz-narrowing-use-fringe-flag' is non-nil 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. (@* "Define Your Own Commands") ** 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-basic-zones', `zz-basic-zones-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-basic-zones 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. (@* "Automatically Create Zones on Region Deactivation") ** Automatically Create Zones on Region Deactivation ** Minor mode `zz-auto-add-region-as-izone-mode' automatically adds the nonempty region as an izone upon its deactivation. The zone is added to the current value of `zz-izones-var'.