Skip to content

Manage numbered examples and references to them in GNU Emacs

License

Notifications You must be signed in to change notification settings

enricoflor/numbex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

numbex.el

https://melpa.org/packages/numbex-badge.svg

numbex is a minor mode for GNU Emacs that helps you manage numbered examples and reference to them in your buffer. It is primarily aimed at linguists, whose practice very often involves having example sentences, formulas, or other objects given with a number that uniquely identifies them in the text:

Consider these two examples:

(1) One example.
(2) Another example.

If you look at (1)...

Now, this very good practice starts to get annoying when taking notes in plain text. Say for instance that you want to switch the two examples above, or that you want to add another example before them. To keep things tidy and unambiguous you’d have to change the number of every other example, and also the number of every reference! If you make a mistake or you forget anything, you will end up with a mess and there is no way to know what refers to what from the numbers alone. numbex-mode takes care of all this for you.

numbex should work in any major mode, and in particular it should not conflict with the default behavior of org-mode. The fundamental idea behind numbex is that the buffer text proper doesn’t contain any numbering itself, but only numbex items (that is, portions of buffer content a certain form): the numbering is added to the buffer as text properties. The command numbex-toggle-visibility switches the buffer back and forth from a state with and without the numbering.

There are no preset default keybindings, but it is a good idea to define some for the two most common interactive commands: numbex-dwim (aka “do what I mean”) and numbex-toggle-visibility. The other interactive commands, explained below, are numbex-list, numbex-refresh, numbex-toggle-numbering-reset, numbex-convert-to-latex, numbex-convert-from-latex and numbex-write-out-numbers.

This use-package declaration illustrates all possible customizations:

(use-package numbex
  :config
  ;; Set "M-n" as the prefix key for numbex commands
  (define-key global-map (kbd "M-n") numbex-command-map)
  :custom
  (numbex-highlight-unlabeled t)
  (numbex-highlight-duplicates t)
  (numbex-numbering-reset t)
  ;; Enclose numbers between parentheses
  (numbex-delimiters '("(" . ")"))
  ;; Restart numbering from "(1)" at each page break and at each top level
  ;; org-mode heading
  (numbex-numbering-reset-regexps '("" "^\\* "))
  :bind
  (:map numbex-command-map
	("RET" . numbex-dwim)		  ; new item or edit one at point
	("s" . numbex-list)		  ; search items throuh 'occur'
	("r" . numbex-toggle-numbering-reset)
	("M-SPC" . numbex-toggle-visibility) ; show/hide underlying raw items
	("M-p" . numbex-backward-example) ; jump to previous example
	("M-n" . numbex-forward-example)	  ; jump to next example
	("w" . numbex-write-out-numbers)  ; replace items with their number
	("g" . numbex-refresh)))

You can install numbex via Melpa.

Basic usage

A “numbex item” is a buffer substring whose delimiters are {[ and ]}, and whose body consist of a prefix and a label. The prefix determines the kind of item:

PrefixType of item
ex:Example (introduces a new label)
rex:Reference to an example
pex:Reference to immediately preceding example
nex:Reference to immediately following example

The label is the substring after the prefix in the item. So for instance {[ex:first]} is an example item whose label is first. Items can be unlabeled: {[ex:]}. There cannot be whitespace characters inside an item: if you add any, they will be automatically removed. (This is to make sure that an item never ends up broken in different lines, which would cause numbex not to recognize the item anymore.)

numbex-previous-item and numbex-next-item are two commands that let you jump quickly between example items.

While you can manipulate numbex items yourself (either manually or programmatically), it is much more convenient to use the command numbex-dwim (more on this below).

Creating and editing items with numbex-dwim

The main command provided by numbex-mode is numbex-dwim. How this command behaves depends on whether it is invoked while point is on an already existing item or not: in the former case, it will let you edit the item, in the latter, you will be guided in creating a new one.

If point is not already on an item, numbex-dwim will prompt the choice of which item to create:

./screenshots/numbex-do-1.png

If you choose to create a reference, you will be offered completion with the labels that are already being used in the buffer, annotated with the context around the examples (the buffer content between the item and the end of the line where the item is).

{[ex:]} Odd example
{[ex:second-example]} One example.
{[ex:first-example]} Another example.

{[ex:third]} Last example

If you look at {[rex:second-example]} you will see that
{[pex:]} and {[rex:]} resemble this sentence:

./screenshots/numbex-do-select-label-annotation.png

There is a caveat here: if there are two example items labeled with xyz in the buffer, only the first one will show up in the completion selection involved in creating or editing a reference item.

If creating an example, if you choose a label that is already being used you will be asked to confirm your choice:

./screenshots/numbex-do-confirm.png

Evaluating numbex-dwim while point is on an item (regardless of whether labels are currently displayed or not) lets you change the label of the item. Again, if it’s a reference, you will be able to use completion on the existing labels (with annotation, showing you the content of the examples). If you are editing pex: or nex: items, they will be automatically converted in regular references. Finally, if you edit an example by providing a novel label, you will be asked whether you want to update the label of all the items that reference that example automatically.

./screenshots/numbex-do-change-label.png

numbex really tries its best to make sure you use unique labels. If, in editing or creating a new example, you try to give it a label that is not unique, you will be warned and asked whether you want to enter a different one, stick with the non unique one (although that would be a bad idea) or let numbex make the label you wish unique by automatically adding a numerical suffix.

./screenshots/numbex-uniquify.png

Numbering of references

The numbering of references depends on several factors. Labeled reference items (like {[rex:first-example]}) inherit the same number as the corresponding example item (in this case, {[ex:first-example]}). Unlabeled ones (like {[rex:]}) receive the same number as the example item immediately preceding them.

The special reference items pex: and nex:, automatically receive an uninformative label corresponding to the number they are assigned. This is because numbex automatically assigns to them the same number as the example that immediately precedes or follows them (respectively). If the state of the buffer changes as to which example precedes or follows them, their label will change accordingly. This is why it would make no sense (and in a way it would be confusing) them to have a label: if any label is found, numbex will remove it. However, if you edit a pex: or nex: item with numbex-dwim, it will be automatically converted in a regular reference item with the new label you selected. So point is on {[pex:]}, selecting example as a label with numbex-dwim will replace the item with {[rex:example]}.

Assuming that this is the whole buffer:

{[ex:]} Odd example
{[ex:second-example]} One example.
{[ex:first-example]} Another example.

If you look at {[rex:second-example]} you will see that {[pex:]}...

this is how numbex will number the items:

(1) Odd example.
(2) One example.
(3) Another example.

If you look at (2) you will see that (3)...

All interactive commands

All the interactive commands are listed below. The user can choose a prefix for numbex-command-map and then bind commands to keys in that map.

  • numbex-dwim
  • numbex-list
  • numbex-refresh
  • numbex-forward-example
  • numbex-backward-example
  • numbex-toggle-visibility
  • numbex-toggle-numbering-reset
  • numbex-write-out-numbers
  • numbex-edit
  • numbex-new-item
  • numbex-new-example
  • numbex-new-reference
  • numbex-new-reference-to-previous
  • numbex-new-reference-to-next

Appearance

Absolute vs. relative numbering of examples

How examples are numbered when numbex-mode is first activated depends on the value of the variable numbex-numbering-reset (by default t) and by numbex-numbering-reset-regexps (by default (" ")).

If numbex-numbering-reset is nil, examples, whether labeled or not, are just numbered sequentially, starting with (1) on the very first example in the buffer. The counter never restarts from (1). The value of numbex-numbering-reset (a buffer-local variable) can be switched interactively with the command numbex-toggle-numbering-reset.

The buffer-local variable numbex-numbering-reset-regexps is a list of regexps, each of which determines where the numbering should restart. Its default value of (" ") means that the numbering only restarts at page breaks. You can add to the list whatever regexp you want, and you can assign a value as a file local variable. So for instance having this magic comment at the bottom of the buffer will cause the numbering to restart at each page break and at each 1st and 2nd level org-mode heading:

;; Local Variables:
;; numbex-numbering-reset-regexps: ("" "^\\*\\*+ ")
;; End:

It is important to note that only the numbering is affected by numbex-numbering-reset. This means that labels have to be unique across the buffer no matter how this variable is set, and that you can always reference examples outside of the page or the narrowed buffered, if you have the label, and that the number on such reference will be the one that the example you are referencing has—and this could cause local ambiguities (with relative numbering, there can be several distinct examples in the buffer that are numbered with, say, (1)). Furthermore, since the information about the buffer is always retrievable, you will always get the context of an example you are referencing, even with relative numbering and when referencing an example that is outside of the accessible portion of the buffer.

Delimiters

The variable numbex-delimiters is a cons cell of strings determining the appearance of the items. The default value is ("(" . ")"), which means that the numbers appear between parentheses. This value can be set file locally. For example, if I want numbers to be between brackets like [1], I can add:

;; Local Variables:
;; numbex-delimiters: ("[" . "]")
;; End:

Other conveniences

When point is on an item, the underlying label is displayed in the echo area. If the item is a reference item, the echo area will also display the context of the corresponding example item (its line). This way, you will always have a clue as to what is referred to by the item at point:

./screenshots/numbex-display-label.png

Right after any invocation of numbex-dwim you will be reminded of the existence of duplicate labels (non-empty labels that are being used by more than one example item) in the echo area:

./screenshots/numbex-duplicate-found.png

These two features work even if the buffer is currently narrowed and the example item you are referring to or the duplicate label are outside of the narrowed portion of the buffer (that is, they are currently inaccessible). This way, the chances of you ending up with a mess once you widen the buffer again are minimized.

Syntax highlighting

By default, numbex color-codes numbers corresponding to unlabeled items or to items with a non-unique label when the buffer is displaying the labels. This is done with whatever text property the current theme uses to mark comments and warnings (respectively).

{[ex:]} Odd example
{[ex:second-example]} One example.
{[ex:first-example]} Another example.

If you look at {[rex:second-example]} you will see that
{[pex:]} and {[rex:]} resemble this sentence:

{[ex:second-example]} An example.

./screenshots/numbex-highlighting-01.png ./screenshots/numbex-highlighting-02.png

If you want to change this default behavior, set the variables numbex-highlight-unlabeled and/or numbex-highlight-duplicates to nil.

Exporting

You might want to export the notes you have maintained with numbex in another plain text file where the numbers are actual text content instead of text properties (for instance, you want to send a plain text email with numbered examples). This is a destructive operation: it will necessarily remove information that cannot be restored (namely, the labels). Therefore, numbex-write-out-numbers will save the content of the buffer in a file (whose name is the name of the current buffer prefixed by nb-), where all the numbex items are actually replaced by the numbers.

Searching

Finally, numbex-list is a convenient wrapper around occur that lets you examine the items in the buffer: use it to have, in other window, a grep-like overview of the lines that contain any item, any example, any reference, any item with a non-unique label, any unlabeled item, or, when evaluated when point is on an item, any item with the same label as the item at point.

Dealing with large files

The numbering of items and the collection of information about labels (duplicates etc.) is performed by numbex-refresh. By default, this operation is performed automatically if the current buffer is in numbex-mode at these moments:

  • when numbex-mode is activated
  • every time Emacs is idle for 0.3 seconds (enough time not to be in the way of your typing), if the buffer has changed;
  • when the buffer is saved or auto-saved;
  • right after any time one of these functions is evaluated:
    • numbex-dwim
    • numbex-toggle-visibility

If you have less than a thousand numbex items in your buffer, you shouldn’t notice any significant lag. If you have 500, numbex-refresh should take approximately 0.05 seconds, which makes the process just about imperceptible.

However, if when you activate numbex-mode more than 1000 numbex items are found, you will be asked whether you want to disable automatic refresh. If you disable it, numbex-refresh will only be evaluated when you save the buffer (or when it is auto-saved) and of course when you interactively call it as a command. Regardless of what you answer to that question, any time that there are more than 1000 numbex items in the buffer, numbex-refresh won’t be evaluated on the idle-timer every 0.3 seconds. It is unlikely, however, that you will ever have this many examples and references in a single buffer. If you plan to keep notes with more than ten thousand items… it’s better if you don’t use numbex-mode at all.

About

Manage numbered examples and references to them in GNU Emacs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published