diff options
| author | Richard M. Stallman | 1997-08-30 23:25:29 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1997-08-30 23:25:29 +0000 |
| commit | 228282068d90a8ac238b5e8f4121213ab8df2b04 (patch) | |
| tree | b7bcc8dbb837764bce1635b6b3ac3f49cf645e10 | |
| parent | 80216a47a461ee8aa52872c551241c6a67bad78b (diff) | |
| download | emacs-228282068d90a8ac238b5e8f4121213ab8df2b04.tar.gz emacs-228282068d90a8ac238b5e8f4121213ab8df2b04.zip | |
(format-subtract-regions): New function.
(format-property-increment-region): New function.
(format-deannotate-region): When multiple annotations
go into a single text property, split the outer annotations (with
format-subtract-regions) instead of resetting them; use lists of
regions instead of a single number for the text property start.
(format-deannotate-region): Don't change extents of
enclosing annotations of the same kind.
(format-deannotate-region): Use
property-increment-region to add to numeric properties.
| -rw-r--r-- | lisp/format.el | 258 |
1 files changed, 162 insertions, 96 deletions
diff --git a/lisp/format.el b/lisp/format.el index 1ab15a4bb7c..5dff28edd3c 100644 --- a/lisp/format.el +++ b/lisp/format.el | |||
| @@ -538,97 +538,113 @@ to write these unknown annotations back into the file." | |||
| 538 | 538 | ||
| 539 | ;; Delete the annotation | 539 | ;; Delete the annotation |
| 540 | (delete-region loc end) | 540 | (delete-region loc end) |
| 541 | (if positive | 541 | (cond |
| 542 | ;; Positive annotations are stacked, remembering location | 542 | ;; Positive annotations are stacked, remembering location |
| 543 | (setq open-ans (cons (list name loc) open-ans)) | 543 | (positive (setq open-ans (cons `(,name ((,loc . nil))) open-ans))) |
| 544 | ;; It is a negative annotation: | 544 | ;; It is a negative annotation: |
| 545 | ;; Close the top annotation & add its text property. | 545 | ;; Close the top annotation & add its text property. |
| 546 | ;; If the file's nesting is messed up, the close might not match | 546 | ;; If the file's nesting is messed up, the close might not match |
| 547 | ;; the top thing on the open-annotations stack. | 547 | ;; the top thing on the open-annotations stack. |
| 548 | ;; If no matching annotation is open, just ignore the close. | 548 | ;; If no matching annotation is open, just ignore the close. |
| 549 | (if (not (assoc name open-ans)) | 549 | ((not (assoc name open-ans)) |
| 550 | (message "Extra closing annotation (%s) in file" name) | 550 | (message "Extra closing annotation (%s) in file" name)) |
| 551 | ;; If one is open, but not on the top of the stack, close | 551 | ;; If one is open, but not on the top of the stack, close |
| 552 | ;; the things in between as well. Set `found' when the real | 552 | ;; the things in between as well. Set `found' when the real |
| 553 | ;; one is closed. | 553 | ;; one is closed. |
| 554 | (while (not found) | 554 | (t |
| 555 | (let* ((top (car open-ans)) ; first on stack: should match. | 555 | (while (not found) |
| 556 | (top-name (car top)) | 556 | (let* ((top (car open-ans)) ; first on stack: should match. |
| 557 | (start (car (cdr top))) ; location of start | 557 | (top-name (car top)) ; text property name |
| 558 | (params (cdr (cdr top))) ; parameters | 558 | (top-extents (nth 1 top)) ; property regions |
| 559 | (aalist translations) | 559 | (params (cdr (cdr top))) ; parameters |
| 560 | (matched nil)) | 560 | (aalist translations) |
| 561 | (if (equal name top-name) | 561 | (matched nil)) |
| 562 | (setq found t) | 562 | (if (equal name top-name) |
| 563 | (message "Improper nesting in file.")) | 563 | (setq found t) |
| 564 | ;; Look through property names in TRANSLATIONS | 564 | (message "Improper nesting in file.")) |
| 565 | (while aalist | 565 | ;; Look through property names in TRANSLATIONS |
| 566 | (let ((prop (car (car aalist))) | 566 | (while aalist |
| 567 | (alist (cdr (car aalist)))) | 567 | (let ((prop (car (car aalist))) |
| 568 | ;; And look through values for each property | 568 | (alist (cdr (car aalist)))) |
| 569 | (while alist | 569 | ;; And look through values for each property |
| 570 | (let ((value (car (car alist))) | 570 | (while alist |
| 571 | (ans (cdr (car alist)))) | 571 | (let ((value (car (car alist))) |
| 572 | (if (member top-name ans) | 572 | (ans (cdr (car alist)))) |
| 573 | ;; This annotation is listed, but still have to | 573 | (if (member top-name ans) |
| 574 | ;; check if multiple annotations are satisfied | 574 | ;; This annotation is listed, but still have to |
| 575 | (if (member 'nil (mapcar | 575 | ;; check if multiple annotations are satisfied |
| 576 | (lambda (r) | 576 | (if (member nil (mapcar (lambda (r) |
| 577 | (assoc r open-ans)) | 577 | (assoc r open-ans)) |
| 578 | ans)) | 578 | ans)) |
| 579 | nil ; multiple ans not satisfied | 579 | nil ; multiple ans not satisfied |
| 580 | ;; Yes, all set. | 580 | ;; If there are multiple annotations going |
| 581 | ;; If there are multiple annotations going | 581 | ;; into one text property, split up the other |
| 582 | ;; into one text property, adjust the | 582 | ;; annotations so they apply individually to |
| 583 | ;; begin points of the other annotations | 583 | ;; the other regions. |
| 584 | ;; so that we don't get double marking. | 584 | (setcdr (car top-extents) loc) |
| 585 | (let ((to-reset ans) | 585 | (let ((to-split ans) this-one extents) |
| 586 | this-one) | 586 | (while to-split |
| 587 | (while to-reset | 587 | (setq this-one |
| 588 | (setq this-one | 588 | (assoc (car to-split) open-ans) |
| 589 | (assoc (car to-reset) | 589 | extents (nth 1 this-one)) |
| 590 | (cdr open-ans))) | 590 | (if (not (eq this-one top)) |
| 591 | (if this-one | 591 | (setcar (cdr this-one) |
| 592 | (setcar (cdr this-one) loc)) | 592 | (format-subtract-regions |
| 593 | (setq to-reset (cdr to-reset)))) | 593 | extents top-extents))) |
| 594 | ;; Set loop variables to nil so loop | 594 | (setq to-split (cdr to-split)))) |
| 595 | ;; will exit. | 595 | ;; Set loop variables to nil so loop |
| 596 | (setq alist nil aalist nil matched t | 596 | ;; will exit. |
| 597 | ;; pop annotation off stack. | 597 | (setq alist nil aalist nil matched t |
| 598 | open-ans (cdr open-ans)) | 598 | ;; pop annotation off stack. |
| 599 | (cond | 599 | open-ans (cdr open-ans)) |
| 600 | ;; Check for pseudo-properties | 600 | (let ((extents top-extents) |
| 601 | ((eq prop 'PARAMETER) | 601 | (start (car (car top-extents))) |
| 602 | ;; This is a parameter of the top open ann: | 602 | (loc (cdr (car top-extents)))) |
| 603 | ;; delete text and use as arg. | 603 | (while extents |
| 604 | (if open-ans | 604 | (cond |
| 605 | ;; (If nothing open, discard). | 605 | ;; Check for pseudo-properties |
| 606 | (setq open-ans | 606 | ((eq prop 'PARAMETER) |
| 607 | (cons (append (car open-ans) | 607 | ;; A parameter of the top open ann: |
| 608 | (list | 608 | ;; delete text and use as arg. |
| 609 | (buffer-substring | 609 | (if open-ans |
| 610 | start loc))) | 610 | ;; (If nothing open, discard). |
| 611 | (cdr open-ans)))) | 611 | (setq open-ans |
| 612 | (delete-region start loc)) | 612 | (cons |
| 613 | ((eq prop 'FUNCTION) | 613 | (append (car open-ans) |
| 614 | ;; Not a property, but a function to call. | 614 | (list |
| 615 | (let ((rtn (apply value start loc params))) | 615 | (buffer-substring |
| 616 | (if rtn (setq todo (cons rtn todo))))) | 616 | start loc))) |
| 617 | (t | 617 | (cdr open-ans)))) |
| 618 | ;; Normal property/value pair | 618 | (delete-region start loc)) |
| 619 | (setq todo | 619 | ((eq prop 'FUNCTION) |
| 620 | (cons (list start loc prop value) | 620 | ;; Not a property, but a function. |
| 621 | todo))))))) | 621 | (let ((rtn |
| 622 | (setq alist (cdr alist)))) | 622 | (apply value start loc params))) |
| 623 | (setq aalist (cdr aalist))) | 623 | (if rtn (setq todo (cons rtn todo))))) |
| 624 | (if matched | 624 | (t |
| 625 | nil | 625 | ;; Normal property/value pair |
| 626 | (setq todo | ||
| 627 | (cons (list start loc prop value) | ||
| 628 | todo)))) | ||
| 629 | (setq extents (cdr extents) | ||
| 630 | start (car (car extents)) | ||
| 631 | loc (cdr (car extents)))))))) | ||
| 632 | (setq alist (cdr alist)))) | ||
| 633 | (setq aalist (cdr aalist))) | ||
| 634 | (if (not matched) | ||
| 626 | ;; Didn't find any match for the annotation: | 635 | ;; Didn't find any match for the annotation: |
| 627 | ;; Store as value of text-property `unknown'. | 636 | ;; Store as value of text-property `unknown'. |
| 628 | (setq open-ans (cdr open-ans)) | 637 | (let ((extents top-extents) |
| 629 | (setq todo (cons (list start loc 'unknown top-name) | 638 | (start (car (car top-extents))) |
| 630 | todo)) | 639 | (loc (cdr (car top-extents)))) |
| 631 | (setq unknown-ans (cons name unknown-ans))))))))) | 640 | (while extents |
| 641 | (setq open-ans (cdr open-ans) | ||
| 642 | todo (cons (list start loc 'unknown top-name) | ||
| 643 | todo) | ||
| 644 | unknown-ans (cons name unknown-ans) | ||
| 645 | extents (cdr extents) | ||
| 646 | start (car (car extents)) | ||
| 647 | loc (cdr (car extents)))))))))))) | ||
| 632 | 648 | ||
| 633 | ;; Once entire file has been scanned, add the properties. | 649 | ;; Once entire file has been scanned, add the properties. |
| 634 | (while todo | 650 | (while todo |
| @@ -637,21 +653,71 @@ to write these unknown annotations back into the file." | |||
| 637 | (to (nth 1 item)) | 653 | (to (nth 1 item)) |
| 638 | (prop (nth 2 item)) | 654 | (prop (nth 2 item)) |
| 639 | (val (nth 3 item))) | 655 | (val (nth 3 item))) |
| 640 | 656 | ||
| 641 | (put-text-property | 657 | (if (numberp val) ; add to ambient value if numeric |
| 658 | (format-property-increment-region from to prop val 0) | ||
| 659 | (put-text-property | ||
| 642 | from to prop | 660 | from to prop |
| 643 | (cond ((numberp val) ; add to ambient value if numeric | 661 | (cond ((get prop 'format-list-valued) ; value gets consed onto |
| 644 | (+ val (or (get-text-property from prop) 0))) | ||
| 645 | ((get prop 'format-list-valued) ; value gets consed onto | ||
| 646 | ; list-valued properties | 662 | ; list-valued properties |
| 647 | (let ((prev (get-text-property from prop))) | 663 | (let ((prev (get-text-property from prop))) |
| 648 | (cons val (if (listp prev) prev (list prev))))) | 664 | (cons val (if (listp prev) prev (list prev))))) |
| 649 | (t val)))) ; normally, just set to val. | 665 | (t val))))) ; normally, just set to val. |
| 650 | (setq todo (cdr todo))) | 666 | (setq todo (cdr todo))) |
| 651 | 667 | ||
| 652 | (if unknown-ans | 668 | (if unknown-ans |
| 653 | (message "Unknown annotations: %s" unknown-ans)))))) | 669 | (message "Unknown annotations: %s" unknown-ans)))))) |
| 654 | 670 | ||
| 671 | (defun format-subtract-regions (minu subtra) | ||
| 672 | "Remove the regions in SUBTRAHEND from the regions in MINUEND. A region | ||
| 673 | is a dotted pair (from . to). Both parameters are lists of regions. Each | ||
| 674 | list must contain nonoverlapping, noncontiguous regions, in descending | ||
| 675 | order. The result is also nonoverlapping, noncontiguous, and in descending | ||
| 676 | order. The first element of MINUEND can have a cdr of nil, indicating that | ||
| 677 | the end of that region is not yet known." | ||
| 678 | (let* ((minuend (copy-alist minu)) | ||
| 679 | (subtrahend (copy-alist subtra)) | ||
| 680 | (m (car minuend)) | ||
| 681 | (s (car subtrahend)) | ||
| 682 | results) | ||
| 683 | (while (and minuend subtrahend) | ||
| 684 | (cond | ||
| 685 | ;; The minuend starts after the subtrahend ends; keep it. | ||
| 686 | ((> (car m) (cdr s)) | ||
| 687 | (setq results (cons m results) | ||
| 688 | minuend (cdr minuend) | ||
| 689 | m (car minuend))) | ||
| 690 | ;; The minuend extends beyond the end of the subtrahend. Chop it off. | ||
| 691 | ((or (null (cdr m)) (> (cdr m) (cdr s))) | ||
| 692 | (setq results (cons (cons (1+ (cdr s)) (cdr m)) results)) | ||
| 693 | (setcdr m (cdr s))) | ||
| 694 | ;; The subtrahend starts after the minuend ends; throw it away. | ||
| 695 | ((< (cdr m) (car s)) | ||
| 696 | (setq subtrahend (cdr subtrahend) s (car subtrahend))) | ||
| 697 | ;; The subtrahend extends beyond the end of the minuend. Chop it off. | ||
| 698 | (t ;(<= (cdr m) (cdr s))) | ||
| 699 | (if (>= (car m) (car s)) | ||
| 700 | (setq minuend (cdr minuend) m (car minuend)) | ||
| 701 | (setcdr m (1- (car s))) | ||
| 702 | (setq subtrahend (cdr subtrahend) s (car subtrahend)))))) | ||
| 703 | (nconc (nreverse results) minuend))) | ||
| 704 | |||
| 705 | ;; This should probably go somewhere other than format.el. Then again, | ||
| 706 | ;; indent.el has alter-text-property. NOTE: We can also use | ||
| 707 | ;; next-single-property-change instead of text-property-not-all, but then | ||
| 708 | ;; we have to see if we passed TO. | ||
| 709 | (defun format-property-increment-region (from to prop delta default) | ||
| 710 | "Increment property PROP over the region between FROM and TO by the | ||
| 711 | amount DELTA (which may be negative). If property PROP is nil anywhere | ||
| 712 | in the region, it is treated as though it were DEFAULT." | ||
| 713 | (let ((cur from) val newval next) | ||
| 714 | (while cur | ||
| 715 | (setq val (get-text-property cur prop) | ||
| 716 | newval (+ (or val default) delta) | ||
| 717 | next (text-property-not-all cur to prop val)) | ||
| 718 | (put-text-property cur (or next to) prop newval) | ||
| 719 | (setq cur next)))) | ||
| 720 | |||
| 655 | ;;; | 721 | ;;; |
| 656 | ;;; Encoding | 722 | ;;; Encoding |
| 657 | ;;; | 723 | ;;; |