elisp: Format Conversion Piecemeal

 
 24.12.3 Piecemeal Specification
 -------------------------------
 
 In contrast to the round-trip specification described in the previous
 subsection (SeeFormat Conversion Round-Trip), you can use the
 variables ‘after-insert-file-functions’ and
 ‘write-region-annotate-functions’ to separately control the respective
 reading and writing conversions.
 
    Conversion starts with one representation and produces another
 representation.  When there is only one conversion to do, there is no
 conflict about what to start with.  However, when there are multiple
 conversions involved, conflict may arise when two conversions need to
 start with the same data.
 
    This situation is best understood in the context of converting text
 properties during ‘write-region’.  For example, the character at
 position 42 in a buffer is ‘X’ with a text property ‘foo’.  If the
 conversion for ‘foo’ is done by inserting into the buffer, say, ‘FOO:’,
 then that changes the character at position 42 from ‘X’ to ‘F’.  The
 next conversion will start with the wrong data straight away.
 
    To avoid conflict, cooperative conversions do not modify the buffer,
 but instead specify “annotations”, a list of elements of the form
 ‘(POSITION . STRING)’, sorted in order of increasing POSITION.
 
    If there is more than one conversion, ‘write-region’ merges their
 annotations destructively into one sorted list.  Later, when the text
 from the buffer is actually written to the file, it intermixes the
 specified annotations at the corresponding positions.  All this takes
 place without modifying the buffer.
 
    In contrast, when reading, the annotations intermixed with the text
 are handled immediately.  ‘insert-file-contents’ sets point to the
 beginning of some text to be converted, then calls the conversion
 functions with the length of that text.  These functions should always
 return with point at the beginning of the inserted text.  This approach
 makes sense for reading because annotations removed by the first
 converter can’t be mistakenly processed by a later converter.  Each
 conversion function should scan for the annotations it recognizes,
 remove the annotation, modify the buffer text (to set a text property,
 for example), and return the updated length of the text, as it stands
 after those changes.  The value returned by one function becomes the
 argument to the next function.
 
  -- Variable: write-region-annotate-functions
      A list of functions for ‘write-region’ to call.  Each function in
      the list is called with two arguments: the start and end of the
      region to be written.  These functions should not alter the
      contents of the buffer.  Instead, they should return annotations.
 
      As a special case, a function may return with a different buffer
      current.  Emacs takes this to mean that the current buffer contains
      altered text to be output.  It therefore changes the START and END
      arguments of the ‘write-region’ call, giving them the values of
      ‘point-min’ and ‘point-max’ in the new buffer, respectively.  It
      also discards all previous annotations, because they should have
      been dealt with by this function.
 
  -- Variable: write-region-post-annotation-function
      The value of this variable, if non-‘nil’, should be a function.
      This function is called, with no arguments, after ‘write-region’
      has completed.
 
      If any function in ‘write-region-annotate-functions’ returns with a
      different buffer current, Emacs calls
      ‘write-region-post-annotation-function’ more than once.  Emacs
      calls it with the last buffer that was current, and again with the
      buffer before that, and so on back to the original buffer.
 
      Thus, a function in ‘write-region-annotate-functions’ can create a
      buffer, give this variable the local value of ‘kill-buffer’ in that
      buffer, set up the buffer with altered text, and make the buffer
      current.  The buffer will be killed after ‘write-region’ is done.
 
  -- Variable: after-insert-file-functions
      Each function in this list is called by ‘insert-file-contents’ with
      one argument, the number of characters inserted, and with point at
      the beginning of the inserted text.  Each function should leave
      point unchanged, and return the new character count describing the
      inserted text as modified by the function.
 
    We invite users to write Lisp programs to store and retrieve text
 properties in files, using these hooks, and thus to experiment with
 various data formats and find good ones.  Eventually we hope users will
 produce good, general extensions we can install in Emacs.
 
    We suggest not trying to handle arbitrary Lisp objects as text
 property names or values—because a program that general is probably
 difficult to write, and slow.  Instead, choose a set of possible data
 types that are reasonably flexible, and not too hard to encode.