comparison lisp/format.el @ 215:1f0dabaa0855 r20-4b6

Import from CVS: tag r20-4b6
author cvs
date Mon, 13 Aug 2007 10:07:35 +0200
parents 41ff10fd062f
children 262b8bb4a523
comparison
equal deleted inserted replaced
214:c5d88c05e1e9 215:1f0dabaa0855
20 ;; You should have received a copy of the GNU General Public License 20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs; see the file COPYING. If not, write to the Free 21 ;; along with XEmacs; see the file COPYING. If not, write to the Free
22 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 22 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 ;; 02111-1307, USA. 23 ;; 02111-1307, USA.
24 24
25 ;;; Synched up with: Emacs/Mule zeta. 25 ;;; Synched up with: Emacs 20.2.
26 26
27 ;;; Commentary: 27 ;;; Commentary:
28 28
29 ;; This file is dumped with XEmacs. 29 ;; This file is dumped with XEmacs.
30 30
81 "Content-[Tt]ype:[ \t]*text/enriched" 81 "Content-[Tt]ype:[ \t]*text/enriched"
82 enriched-decode enriched-encode t enriched-mode) 82 enriched-decode enriched-encode t enriched-mode)
83 (text/richtext "Extended MIME obsolete text/richtext format." 83 (text/richtext "Extended MIME obsolete text/richtext format."
84 "Content-[Tt]ype:[ \t]*text/richtext" 84 "Content-[Tt]ype:[ \t]*text/richtext"
85 richtext-decode richtext-encode t enriched-mode) 85 richtext-decode richtext-encode t enriched-mode)
86 (plain "Standard ASCII format, no text properties." 86 (plain "ISO 8859-1 standard format, no text properties."
87 ;; Plain only exists so that there is an obvious neutral choice in 87 ;; Plain only exists so that there is an obvious neutral choice in
88 ;; the completion list. 88 ;; the completion list.
89 nil nil nil nil nil)) 89 nil nil nil nil nil)
90 ;; (ibm "IBM Code Page 850 (DOS)"
91 ;; "1\\(^\\)"
92 ;; "recode ibm-pc:latin1" "recode latin1:ibm-pc" t nil)
93 ;; (mac "Apple Macintosh"
94 ;; "1\\(^\\)"
95 ;; "recode mac:latin1" "recode latin1:mac" t nil)
96 ;; (hp "HP Roman8"
97 ;; "1\\(^\\)"
98 ;; "recode roman8:latin1" "recode latin1:roman8" t nil)
99 ;; (TeX "TeX (encoding)"
100 ;; "1\\(^\\)"
101 ;; iso-tex2iso iso-iso2tex t nil)
102 ;; (gtex "German TeX (encoding)"
103 ;; "1\\(^\\)"
104 ;; iso-gtex2iso iso-iso2gtex t nil)
105 ;; (html "HTML (encoding)"
106 ;; "1\\(^\\)"
107 ;; "recode html:latin1" "recode latin1:html" t nil)
108 ;; (rot13 "rot13"
109 ;; "1\\(^\\)"
110 ;; "tr a-mn-z n-za-m" "tr a-mn-z n-za-m" t nil)
111 ;; (duden "Duden Ersatzdarstellung"
112 ;; "1\\(^\\)"
113 ;; "diac" iso-iso2duden t nil)
114 ;; (de646 "German ASCII (ISO 646)"
115 ;; "1\\(^\\)"
116 ;; "recode iso646-ge:latin1" "recode latin1:iso646-ge" t nil)
117 ;; (denet "net German"
118 ;; "1\\(^\\)"
119 ;; iso-german iso-cvt-read-only t nil)
120 ;; (esnet "net Spanish"
121 ;; "1\\(^\\)"
122 ;; iso-spanish iso-cvt-read-only t nil)
123 )
90 "List of information about understood file formats. 124 "List of information about understood file formats.
91 Elements are of the form \(NAME DOC-STR REGEXP FROM-FN TO-FN MODIFY MODE-FN). 125 Elements are of the form \(NAME DOC-STR REGEXP FROM-FN TO-FN MODIFY MODE-FN).
126
92 NAME is a symbol, which is stored in `buffer-file-format'. 127 NAME is a symbol, which is stored in `buffer-file-format'.
128
93 DOC-STR should be a single line providing more information about the 129 DOC-STR should be a single line providing more information about the
94 format. It is currently unused, but in the future will be shown to 130 format. It is currently unused, but in the future will be shown to
95 the user if they ask for more information. 131 the user if they ask for more information.
132
96 REGEXP is a regular expression to match against the beginning of the file; 133 REGEXP is a regular expression to match against the beginning of the file;
97 it should match only files in that format. 134 it should match only files in that format.
135
98 FROM-FN is called to decode files in that format; it gets two args, BEGIN 136 FROM-FN is called to decode files in that format; it gets two args, BEGIN
99 and END, and can make any modifications it likes, returning the new 137 and END, and can make any modifications it likes, returning the new
100 end. It must make sure that the beginning of the file no longer 138 end. It must make sure that the beginning of the file no longer
101 matches REGEXP, or else it will get called again. 139 matches REGEXP, or else it will get called again.
140 Alternatively, FROM-FN can be a string, which specifies a shell command
141 (including options) to be used as a filter to perform the conversion.
142
102 TO-FN is called to encode a region into that format; it is passed three 143 TO-FN is called to encode a region into that format; it is passed three
103 arguments: BEGIN, END, and BUFFER. BUFFER is the original buffer that 144 arguments: BEGIN, END, and BUFFER. BUFFER is the original buffer that
104 the data being written came from, which the function could use, for 145 the data being written came from, which the function could use, for
105 example, to find the values of local variables. TO-FN should either 146 example, to find the values of local variables. TO-FN should either
106 return a list of annotations like `write-region-annotate-functions', 147 return a list of annotations like `write-region-annotate-functions',
107 or modify the region and return the new end. 148 or modify the region and return the new end.
149 Alternatively, TO-FN can be a string, which specifies a shell command
150 (including options) to be used as a filter to perform the conversion.
151
108 MODIFY, if non-nil, means the TO-FN wants to modify the region. If nil, 152 MODIFY, if non-nil, means the TO-FN wants to modify the region. If nil,
109 TO-FN will not make any changes but will instead return a list of 153 TO-FN will not make any changes but will instead return a list of
110 annotations. 154 annotations.
155
111 MODE-FN, if specified, is called when visiting a file with that format.") 156 MODE-FN, if specified, is called when visiting a file with that format.")
112 157
113 ;;; Basic Functions (called from Lisp) 158 ;;; Basic Functions (called from Lisp)
114 159
115 (defun format-annotate-function (format from to) 160 (defun format-encode-run-method (method from to &optional buffer)
161 "Translate using function or shell script METHOD the text from FROM to TO.
162 If METHOD is a string, it is a shell command;
163 otherwise, it should be a Lisp function.
164 BUFFER should be the buffer that the output originally came from."
165 (if (stringp method)
166 (save-current-buffer
167 (set-buffer buffer)
168 (with-output-to-temp-buffer "*Format Errors*"
169 (shell-command-on-region from to method t nil standard-output))
170 (point))
171 (funcall method from to buffer)))
172
173 (defun format-decode-run-method (method from to &optional buffer)
174 "Decode using function or shell script METHOD the text from FROM to TO.
175 If METHOD is a string, it is a shell command;
176 otherwise, it should be a Lisp function."
177 (if (stringp method)
178 (progn
179 (with-output-to-temp-buffer "*Format Errors*"
180 (shell-command-on-region from to method t nil standard-output))
181 (point))
182 (funcall method from to)))
183
184 (defun format-annotate-function (format from to orig-buf)
116 "Returns annotations for writing region as FORMAT. 185 "Returns annotations for writing region as FORMAT.
117 FORMAT is a symbol naming one of the formats defined in `format-alist', 186 FORMAT is a symbol naming one of the formats defined in `format-alist',
118 it must be a single symbol, not a list like `buffer-file-format'. 187 it must be a single symbol, not a list like `buffer-file-format'.
119 FROM and TO delimit the region to be operated on in the current buffer. 188 FROM and TO delimit the region to be operated on in the current buffer.
189 ORIG-BUF is the original buffer that the data came from.
120 This function works like a function on `write-region-annotate-functions': 190 This function works like a function on `write-region-annotate-functions':
121 it either returns a list of annotations, or returns with a different buffer 191 it either returns a list of annotations, or returns with a different buffer
122 current, which contains the modified text to write. 192 current, which contains the modified text to write.
123 193
124 For most purposes, consider using `format-encode-region' instead." 194 For most purposes, consider using `format-encode-region' instead."
132 ;; To-function wants to modify region. Copy to safe place. 202 ;; To-function wants to modify region. Copy to safe place.
133 (let ((copy-buf (get-buffer-create " *Format Temp*"))) 203 (let ((copy-buf (get-buffer-create " *Format Temp*")))
134 (copy-to-buffer copy-buf from to) 204 (copy-to-buffer copy-buf from to)
135 (set-buffer copy-buf) 205 (set-buffer copy-buf)
136 (format-insert-annotations write-region-annotations-so-far from) 206 (format-insert-annotations write-region-annotations-so-far from)
137 (funcall to-fn (point-min) (point-max)) 207 (format-encode-run-method to-fn (point-min) (point-max) orig-buf)
138 nil) 208 nil)
139 ;; Otherwise just call function, it will return annotations. 209 ;; Otherwise just call function, it will return annotations.
140 (funcall to-fn from to))))) 210 (funcall to-fn from to orig-buf)))))
141 211
142 (defun format-decode (format length &optional visit-flag) 212 (defun format-decode (format length &optional visit-flag)
143 ;; This function is called by insert-file-contents whenever a file is read. 213 ;; This function is called by insert-file-contents whenever a file is read.
144 "Decode text from any known FORMAT. 214 "Decode text from any known FORMAT.
145 FORMAT is a symbol appearing in `format-alist' or a list of such symbols, 215 FORMAT is a symbol appearing in `format-alist' or a list of such symbols,
169 (if (and regexp (looking-at regexp) 239 (if (and regexp (looking-at regexp)
170 (< (match-end 0) (+ begin length))) 240 (< (match-end 0) (+ begin length)))
171 (progn 241 (progn
172 (setq format (cons (car f) format)) 242 (setq format (cons (car f) format))
173 ;; Decode it 243 ;; Decode it
174 (if (nth 3 f) (setq end (funcall (nth 3 f) begin end))) 244 (if (nth 3 f)
245 (setq end (format-decode-run-method (nth 3 f) begin end)))
175 ;; Call visit function if required 246 ;; Call visit function if required
176 (if (and visit-flag (nth 6 f)) (funcall (nth 6 f) 1)) 247 (if (and visit-flag (nth 6 f)) (funcall (nth 6 f) 1))
177 ;; Safeguard against either of the functions changing pt. 248 ;; Safeguard against either of the functions changing pt.
178 (goto-char p) 249 (goto-char p)
179 ;; Rewind list to look for another format 250 ;; Rewind list to look for another format
184 (let ((do format) f) 255 (let ((do format) f)
185 (while do 256 (while do
186 (or (setq f (assq (car do) format-alist)) 257 (or (setq f (assq (car do) format-alist))
187 (error "Unknown format" (car do))) 258 (error "Unknown format" (car do)))
188 ;; Decode: 259 ;; Decode:
189 (if (nth 3 f) (setq end (funcall (nth 3 f) begin end))) 260 (if (nth 3 f)
261 (setq end (format-decode-run-method (nth 3 f) begin end)))
190 ;; Call visit function if required 262 ;; Call visit function if required
191 (if (and visit-flag (nth 6 f)) (funcall (nth 6 f) 1)) 263 (if (and visit-flag (nth 6 f)) (funcall (nth 6 f) 1))
192 (setq do (cdr do))))) 264 (setq do (cdr do)))))
193 (if visit-flag 265 (if visit-flag
194 (setq buffer-file-format format)) 266 (setq buffer-file-format format))
251 (modify (nth 5 info)) 323 (modify (nth 5 info))
252 ;; result 324 ;; result
253 ) 325 )
254 (if to-fn 326 (if to-fn
255 (if modify 327 (if modify
256 (setq end (funcall to-fn beg end (current-buffer))) 328 (setq end (format-encode-run-method to-fn beg end
329 (current-buffer)))
257 (format-insert-annotations 330 (format-insert-annotations
258 (funcall to-fn beg end (current-buffer))))) 331 (funcall to-fn beg end (current-buffer)))))
259 (setq format (cdr format))))))) 332 (setq format (cdr format)))))))
260 333
261 (defun format-write-file (filename format) 334 (defun format-write-file (filename format)
483 (positive (nth 3 next)) 556 (positive (nth 3 next))
484 (found nil)) 557 (found nil))
485 558
486 ;; Delete the annotation 559 ;; Delete the annotation
487 (delete-region loc end) 560 (delete-region loc end)
488 (if positive 561 (cond
489 ;; Positive annotations are stacked, remembering location 562 ;; Positive annotations are stacked, remembering location
490 (setq open-ans (cons (list name loc) open-ans)) 563 (positive (setq open-ans (cons `(,name ((,loc . nil))) open-ans)))
491 ;; It is a negative annotation: 564 ;; It is a negative annotation:
492 ;; Close the top annotation & add its text property. 565 ;; Close the top annotation & add its text property.
493 ;; If the file's nesting is messed up, the close might not match 566 ;; If the file's nesting is messed up, the close might not match
494 ;; the top thing on the open-annotations stack. 567 ;; the top thing on the open-annotations stack.
495 ;; If no matching annotation is open, just ignore the close. 568 ;; If no matching annotation is open, just ignore the close.
496 (if (not (assoc name open-ans)) 569 ((not (assoc name open-ans))
497 (message "Extra closing annotation (%s) in file" name) 570 (message "Extra closing annotation (%s) in file" name))
498 ;; If one is open, but not on the top of the stack, close 571 ;; If one is open, but not on the top of the stack, close
499 ;; the things in between as well. Set `found' when the real 572 ;; the things in between as well. Set `found' when the real
500 ;; one is closed. 573 ;; one is closed.
501 (while (not found) 574 (t
502 (let* ((top (car open-ans)) ; first on stack: should match. 575 (while (not found)
503 (top-name (car top)) 576 (let* ((top (car open-ans)) ; first on stack: should match.
504 (start (car (cdr top))) ; location of start 577 (top-name (car top)) ; text property name
505 (params (cdr (cdr top))) ; parameters 578 (top-extents (nth 1 top)) ; property regions
506 (aalist translations) 579 (params (cdr (cdr top))) ; parameters
507 (matched nil)) 580 (aalist translations)
508 (if (equal name top-name) 581 (matched nil))
509 (setq found t) 582 (if (equal name top-name)
510 (message "Improper nesting in file.")) 583 (setq found t)
511 ;; Look through property names in TRANSLATIONS 584 (message "Improper nesting in file."))
512 (while aalist 585 ;; Look through property names in TRANSLATIONS
513 (let ((prop (car (car aalist))) 586 (while aalist
514 (alist (cdr (car aalist)))) 587 (let ((prop (car (car aalist)))
515 ;; And look through values for each property 588 (alist (cdr (car aalist))))
516 (while alist 589 ;; And look through values for each property
517 (let ((value (car (car alist))) 590 (while alist
518 (ans (cdr (car alist)))) 591 (let ((value (car (car alist)))
519 (if (member top-name ans) 592 (ans (cdr (car alist))))
520 ;; This annotation is listed, but still have to 593 (if (member top-name ans)
521 ;; check if multiple annotations are satisfied 594 ;; This annotation is listed, but still have to
522 (if (member 'nil (mapcar 595 ;; check if multiple annotations are satisfied
523 (lambda (r) 596 (if (member nil (mapcar (lambda (r)
524 (assoc r open-ans)) 597 (assoc r open-ans))
525 ans)) 598 ans))
526 nil ; multiple ans not satisfied 599 nil ; multiple ans not satisfied
527 ;; Yes, all set. 600 ;; If there are multiple annotations going
528 ;; If there are multiple annotations going 601 ;; into one text property, split up the other
529 ;; into one text property, adjust the 602 ;; annotations so they apply individually to
530 ;; begin points of the other annotations 603 ;; the other regions.
531 ;; so that we don't get double marking. 604 (setcdr (car top-extents) loc)
532 (let ((to-reset ans) 605 (let ((to-split ans) this-one extents)
533 this-one) 606 (while to-split
534 (while to-reset 607 (setq this-one
535 (setq this-one 608 (assoc (car to-split) open-ans)
536 (assoc (car to-reset) 609 extents (nth 1 this-one))
537 (cdr open-ans))) 610 (if (not (eq this-one top))
538 (if this-one 611 (setcar (cdr this-one)
539 (setcar (cdr this-one) loc)) 612 (format-subtract-regions
540 (setq to-reset (cdr to-reset)))) 613 extents top-extents)))
541 ;; Set loop variables to nil so loop 614 (setq to-split (cdr to-split))))
542 ;; will exit. 615 ;; Set loop variables to nil so loop
543 (setq alist nil aalist nil matched t 616 ;; will exit.
544 ;; pop annotation off stack. 617 (setq alist nil aalist nil matched t
545 open-ans (cdr open-ans)) 618 ;; pop annotation off stack.
546 (cond 619 open-ans (cdr open-ans))
547 ;; Check for pseudo-properties 620 (let ((extents top-extents)
548 ((eq prop 'PARAMETER) 621 (start (car (car top-extents)))
549 ;; This is a parameter of the top open ann: 622 (loc (cdr (car top-extents))))
550 ;; delete text and use as arg. 623 (while extents
551 (if open-ans 624 (cond
552 ;; (If nothing open, discard). 625 ;; Check for pseudo-properties
553 (setq open-ans 626 ((eq prop 'PARAMETER)
554 (cons (append (car open-ans) 627 ;; A parameter of the top open ann:
555 (list 628 ;; delete text and use as arg.
556 (buffer-substring 629 (if open-ans
557 start loc))) 630 ;; (If nothing open, discard).
558 (cdr open-ans)))) 631 (setq open-ans
559 (delete-region start loc)) 632 (cons
560 ((eq prop 'FUNCTION) 633 (append (car open-ans)
561 ;; Not a property, but a function to call. 634 (list
562 (let ((rtn (apply value start loc params))) 635 (buffer-substring
563 (if rtn (setq todo (cons rtn todo))))) 636 start loc)))
564 (t 637 (cdr open-ans))))
565 ;; Normal property/value pair 638 (delete-region start loc))
566 (setq todo 639 ((eq prop 'FUNCTION)
567 (cons (list start loc prop value) 640 ;; Not a property, but a function.
568 todo))))))) 641 (let ((rtn
569 (setq alist (cdr alist)))) 642 (apply value start loc params)))
570 (setq aalist (cdr aalist))) 643 (if rtn (setq todo (cons rtn todo)))))
571 (if matched 644 (t
572 nil 645 ;; Normal property/value pair
646 (setq todo
647 (cons (list start loc prop value)
648 todo))))
649 (setq extents (cdr extents)
650 start (car (car extents))
651 loc (cdr (car extents))))))))
652 (setq alist (cdr alist))))
653 (setq aalist (cdr aalist)))
654 (if (not matched)
573 ;; Didn't find any match for the annotation: 655 ;; Didn't find any match for the annotation:
574 ;; Store as value of text-property `unknown'. 656 ;; Store as value of text-property `unknown'.
575 (setq open-ans (cdr open-ans)) 657 (let ((extents top-extents)
576 (setq todo (cons (list start loc 'unknown top-name) 658 (start (car (car top-extents)))
577 todo)) 659 (loc (cdr (car top-extents))))
578 (setq unknown-ans (cons name unknown-ans))))))))) 660 (while extents
661 (setq open-ans (cdr open-ans)
662 todo (cons (list start loc 'unknown top-name)
663 todo)
664 unknown-ans (cons name unknown-ans)
665 extents (cdr extents)
666 start (car (car extents))
667 loc (cdr (car extents))))))))))))
579 668
580 ;; Once entire file has been scanned, add the properties. 669 ;; Once entire file has been scanned, add the properties.
581 (while todo 670 (while todo
582 (let* ((item (car todo)) 671 (let* ((item (car todo))
583 (from (nth 0 item)) 672 (from (nth 0 item))
584 (to (nth 1 item)) 673 (to (nth 1 item))
585 (prop (nth 2 item)) 674 (prop (nth 2 item))
586 (val (nth 3 item))) 675 (val (nth 3 item)))
587 676
588 (put-text-property 677 (if (numberp val) ; add to ambient value if numeric
678 (format-property-increment-region from to prop val 0)
679 (put-text-property
589 from to prop 680 from to prop
590 (cond ((numberp val) ; add to ambient value if numeric 681 (cond ((get prop 'format-list-valued) ; value gets consed onto
591 (+ val (or (get-text-property from prop) 0)))
592 ((get prop 'format-list-valued) ; value gets consed onto
593 ; list-valued properties 682 ; list-valued properties
594 (let ((prev (get-text-property from prop))) 683 (let ((prev (get-text-property from prop)))
595 (cons val (if (listp prev) prev (list prev))))) 684 (cons val (if (listp prev) prev (list prev)))))
596 (t val)))) ; normally, just set to val. 685 (t val))))) ; normally, just set to val.
597 (setq todo (cdr todo))) 686 (setq todo (cdr todo)))
598 687
599 (if unknown-ans 688 (if unknown-ans
600 (message "Unknown annotations: %s" unknown-ans)))))) 689 (message "Unknown annotations: %s" unknown-ans))))))
690
691 (defun format-subtract-regions (minu subtra)
692 "Remove the regions in SUBTRAHEND from the regions in MINUEND. A region
693 is a dotted pair (from . to). Both parameters are lists of regions. Each
694 list must contain nonoverlapping, noncontiguous regions, in descending
695 order. The result is also nonoverlapping, noncontiguous, and in descending
696 order. The first element of MINUEND can have a cdr of nil, indicating that
697 the end of that region is not yet known."
698 (let* ((minuend (copy-alist minu))
699 (subtrahend (copy-alist subtra))
700 (m (car minuend))
701 (s (car subtrahend))
702 results)
703 (while (and minuend subtrahend)
704 (cond
705 ;; The minuend starts after the subtrahend ends; keep it.
706 ((> (car m) (cdr s))
707 (setq results (cons m results)
708 minuend (cdr minuend)
709 m (car minuend)))
710 ;; The minuend extends beyond the end of the subtrahend. Chop it off.
711 ((or (null (cdr m)) (> (cdr m) (cdr s)))
712 (setq results (cons (cons (1+ (cdr s)) (cdr m)) results))
713 (setcdr m (cdr s)))
714 ;; The subtrahend starts after the minuend ends; throw it away.
715 ((< (cdr m) (car s))
716 (setq subtrahend (cdr subtrahend) s (car subtrahend)))
717 ;; The subtrahend extends beyond the end of the minuend. Chop it off.
718 (t ;(<= (cdr m) (cdr s)))
719 (if (>= (car m) (car s))
720 (setq minuend (cdr minuend) m (car minuend))
721 (setcdr m (1- (car s)))
722 (setq subtrahend (cdr subtrahend) s (car subtrahend))))))
723 (nconc (nreverse results) minuend)))
724
725 ;; This should probably go somewhere other than format.el. Then again,
726 ;; indent.el has alter-text-property. NOTE: We can also use
727 ;; next-single-property-change instead of text-property-not-all, but then
728 ;; we have to see if we passed TO.
729 (defun format-property-increment-region (from to prop delta default)
730 "Increment property PROP over the region between FROM and TO by the
731 amount DELTA (which may be negative). If property PROP is nil anywhere
732 in the region, it is treated as though it were DEFAULT."
733 (let ((cur from) val newval next)
734 (while cur
735 (setq val (get-text-property cur prop)
736 newval (+ (or val default) delta)
737 next (text-property-not-all cur to prop val))
738 (put-text-property cur (or next to) prop newval)
739 (setq cur next))))
601 740
602 ;;; 741 ;;;
603 ;;; Encoding 742 ;;; Encoding
604 ;;; 743 ;;;
605 744
773 (let ((prop-alist (cdr (assoc prop trans))) 912 (let ((prop-alist (cdr (assoc prop trans)))
774 ;; default 913 ;; default
775 ) 914 )
776 (if (not prop-alist) 915 (if (not prop-alist)
777 nil 916 nil
778 ;; If property is numeric, nil means 0
779 (cond ((and (numberp old) (null new))
780 (setq new 0))
781 ((and (numberp new) (null old))
782 (setq old 0)))
783 ;; If either old or new is a list, have to treat both that way. 917 ;; If either old or new is a list, have to treat both that way.
784 (if (or (consp old) (consp new)) 918 (if (or (consp old) (consp new))
785 (let* ((old (if (listp old) old (list old))) 919 (let* ((old (if (listp old) old (list old)))
786 (new (if (listp new) new (list new))) 920 (new (if (listp new) new (list new)))
787 ;; (tail (format-common-tail old new)) 921 ;; (tail (format-common-tail old new))
803 937
804 (defun format-annotate-atomic-property-change (prop-alist old new) 938 (defun format-annotate-atomic-property-change (prop-alist old new)
805 "Internal function annotate a single property change. 939 "Internal function annotate a single property change.
806 PROP-ALIST is the relevant segment of a TRANSLATIONS list. 940 PROP-ALIST is the relevant segment of a TRANSLATIONS list.
807 OLD and NEW are the values." 941 OLD and NEW are the values."
808 (cond 942 (let (num-ann)
809 ;; Numerical annotation - use difference 943 ;; If old and new values are numbers,
810 ((and (numberp old) (numberp new)) 944 ;; look for a number in PROP-ALIST.
811 (let* ((entry (progn 945 (if (and (or (null old) (numberp old))
812 (while (and (car (car prop-alist)) 946 (or (null new) (numberp new)))
813 (not (numberp (car (car prop-alist))))) 947 (progn
814 (setq prop-alist (cdr prop-alist))) 948 (setq num-ann prop-alist)
815 (car prop-alist))) 949 (while (and num-ann (not (numberp (car (car num-ann)))))
816 (increment (car (car prop-alist))) 950 (setq num-ann (cdr num-ann)))))
817 (n (ceiling (/ (float (- new old)) (float increment)))) 951 (if num-ann
818 (anno (car (cdr (car prop-alist))))) 952 ;; Numerical annotation - use difference
819 (if (> n 0) 953 (progn
820 (cons nil (make-list n anno)) 954 ;; If property is numeric, nil means 0
821 (cons (make-list (- n) anno) nil)))) 955 (cond ((and (numberp old) (null new))
822 956 (setq new 0))
823 ;; Standard annotation 957 ((and (numberp new) (null old))
824 (t (let ((close (and old (cdr (assoc old prop-alist)))) 958 (setq old 0)))
959
960 (let* ((entry (car num-ann))
961 (increment (car entry))
962 (n (ceiling (/ (float (- new old)) (float increment))))
963 (anno (car (cdr entry))))
964 (if (> n 0)
965 (cons nil (make-list n anno))
966 (cons (make-list (- n) anno) nil))))
967
968 ;; Standard annotation
969 (let ((close (and old (cdr (assoc old prop-alist))))
825 (open (and new (cdr (assoc new prop-alist))))) 970 (open (and new (cdr (assoc new prop-alist)))))
826 (if (or close open) 971 (if (or close open)
827 (format-make-relatively-unique close open) 972 (format-make-relatively-unique close open)
828 ;; Call "Default" function, if any 973 ;; Call "Default" function, if any
829 (let ((default (assq nil prop-alist))) 974 (let ((default (assq nil prop-alist)))