aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard M. Stallman1993-07-31 21:58:03 +0000
committerRichard M. Stallman1993-07-31 21:58:03 +0000
commit7ce503fdda34ee029bb1862c71cb756db5cac791 (patch)
treee8a4f8647d1a49e1662ec11b4df82d0012502bbf
parent58943db0069c771583df071c58f88ece0cdf8820 (diff)
downloademacs-7ce503fdda34ee029bb1862c71cb756db5cac791.tar.gz
emacs-7ce503fdda34ee029bb1862c71cb756db5cac791.zip
(adjust_intervals_for_insertion): Handle insertion
between two unlike intervals via merge_properties_sticky. (merge_properties_sticky): New function. (graft_intervals_into_buffer): Leave handling of `sticky'-ness to adjust_intervals_for_insertion, then merge properties of the inserted text onto the old ones. (textget_direct): New function. (set_point): Fix calculating of fromprev. (verify_interval_modification): Check for `read-only' property and take its `sticky'-ness into account. (set_point): Ignore `invisible' property unless property value is `hidden'.
-rw-r--r--src/intervals.c442
1 files changed, 321 insertions, 121 deletions
diff --git a/src/intervals.c b/src/intervals.c
index b39709e0b9e..42f35942137 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -5,7 +5,7 @@ This file is part of GNU Emacs.
5 5
6GNU Emacs is free software; you can redistribute it and/or modify 6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by 7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option) 8the Free Software Foundation; either version 2, or (at your option)
9any later version. 9any later version.
10 10
11GNU Emacs is distributed in the hope that it will be useful, 11GNU Emacs is distributed in the hope that it will be useful,
@@ -43,16 +43,16 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
43#include "intervals.h" 43#include "intervals.h"
44#include "buffer.h" 44#include "buffer.h"
45 45
46/* The rest of the file is within this conditional. */ 46/* The rest of the file is within this conditional. */
47#ifdef USE_TEXT_PROPERTIES 47#ifdef USE_TEXT_PROPERTIES
48 48
49/* Factor for weight-balancing interval trees. */ 49/* Factor for weight-balancing interval trees. */
50Lisp_Object interval_balance_threshold; 50Lisp_Object interval_balance_threshold;
51 51
52/* Utility functions for intervals. */ 52/* Utility functions for intervals. */
53 53
54 54
55/* Create the root interval of some object, a buffer or string. */ 55/* Create the root interval of some object, a buffer or string. */
56 56
57INTERVAL 57INTERVAL
58create_root_interval (parent) 58create_root_interval (parent)
@@ -125,7 +125,7 @@ merge_properties (source, target)
125} 125}
126 126
127/* Return 1 if the two intervals have the same properties, 127/* Return 1 if the two intervals have the same properties,
128 0 otherwise. */ 128 0 otherwise. */
129 129
130int 130int
131intervals_equal (i0, i1) 131intervals_equal (i0, i1)
@@ -147,27 +147,27 @@ intervals_equal (i0, i1)
147 i0_cdr = i0->plist; 147 i0_cdr = i0->plist;
148 while (!NILP (i0_cdr)) 148 while (!NILP (i0_cdr))
149 { 149 {
150 /* Lengths of the two plists were unequal */ 150 /* Lengths of the two plists were unequal. */
151 if (i1_len == 0) 151 if (i1_len == 0)
152 return 0; 152 return 0;
153 153
154 i0_sym = Fcar (i0_cdr); 154 i0_sym = Fcar (i0_cdr);
155 i1_val = Fmemq (i0_sym, i1->plist); 155 i1_val = Fmemq (i0_sym, i1->plist);
156 156
157 /* i0 has something i1 doesn't */ 157 /* i0 has something i1 doesn't. */
158 if (EQ (i1_val, Qnil)) 158 if (EQ (i1_val, Qnil))
159 return 0; 159 return 0;
160 160
161 /* i0 and i1 both have sym, but it has different values in each */ 161 /* i0 and i1 both have sym, but it has different values in each. */
162 i0_cdr = Fcdr (i0_cdr); 162 i0_cdr = Fcdr (i0_cdr);
163 if (! EQ (i1_val, Fcar (i0_cdr))) 163 if (! EQ (Fcar (Fcdr (i1_val)), Fcar (i0_cdr)))
164 return 0; 164 return 0;
165 165
166 i0_cdr = Fcdr (i0_cdr); 166 i0_cdr = Fcdr (i0_cdr);
167 i1_len--; 167 i1_len--;
168 } 168 }
169 169
170 /* Lengths of the two plists were unequal */ 170 /* Lengths of the two plists were unequal. */
171 if (i1_len > 0) 171 if (i1_len > 0)
172 return 0; 172 return 0;
173 173
@@ -200,7 +200,7 @@ traverse_intervals (tree, position, depth, function, arg)
200} 200}
201 201
202#if 0 202#if 0
203/* These functions are temporary, for debugging purposes only. */ 203/* These functions are temporary, for debugging purposes only. */
204 204
205INTERVAL search_interval, found_interval; 205INTERVAL search_interval, found_interval;
206 206
@@ -279,7 +279,7 @@ rotate_right (interval)
279 INTERVAL B = interval->left; 279 INTERVAL B = interval->left;
280 int len = LENGTH (interval); 280 int len = LENGTH (interval);
281 281
282 /* Deal with any Parent of A; make it point to B. */ 282 /* Deal with any Parent of A; make it point to B. */
283 if (! ROOT_INTERVAL_P (interval)) 283 if (! ROOT_INTERVAL_P (interval))
284 if (AM_LEFT_CHILD (interval)) 284 if (AM_LEFT_CHILD (interval))
285 interval->parent->left = interval->left; 285 interval->parent->left = interval->left;
@@ -287,15 +287,15 @@ rotate_right (interval)
287 interval->parent->right = interval->left; 287 interval->parent->right = interval->left;
288 interval->left->parent = interval->parent; 288 interval->left->parent = interval->parent;
289 289
290 /* B gets the same length as A, since it get A's position in the tree. */ 290 /* B gets the same length as A, since it get A's position in the tree. */
291 interval->left->total_length = interval->total_length; 291 interval->left->total_length = interval->total_length;
292 292
293 /* B becomes the parent of A. */ 293 /* B becomes the parent of A. */
294 i = interval->left->right; 294 i = interval->left->right;
295 interval->left->right = interval; 295 interval->left->right = interval;
296 interval->parent = interval->left; 296 interval->parent = interval->left;
297 297
298 /* A gets c as left child. */ 298 /* A gets c as left child. */
299 interval->left = i; 299 interval->left = i;
300 if (! NULL_INTERVAL_P (i)) 300 if (! NULL_INTERVAL_P (i))
301 i->parent = interval; 301 i->parent = interval;
@@ -322,7 +322,7 @@ rotate_left (interval)
322 INTERVAL B = interval->right; 322 INTERVAL B = interval->right;
323 int len = LENGTH (interval); 323 int len = LENGTH (interval);
324 324
325 /* Deal with the parent of A. */ 325 /* Deal with the parent of A. */
326 if (! ROOT_INTERVAL_P (interval)) 326 if (! ROOT_INTERVAL_P (interval))
327 if (AM_LEFT_CHILD (interval)) 327 if (AM_LEFT_CHILD (interval))
328 interval->parent->left = interval->right; 328 interval->parent->left = interval->right;
@@ -330,7 +330,7 @@ rotate_left (interval)
330 interval->parent->right = interval->right; 330 interval->parent->right = interval->right;
331 interval->right->parent = interval->parent; 331 interval->right->parent = interval->parent;
332 332
333 /* B must have the same total length of A. */ 333 /* B must have the same total length of A. */
334 interval->right->total_length = interval->total_length; 334 interval->right->total_length = interval->total_length;
335 335
336 /* Make B the parent of A */ 336 /* Make B the parent of A */
@@ -359,7 +359,7 @@ rotate_left (interval)
359 result. 359 result.
360 360
361 Note that this does not change the position of INTERVAL; if it is a root, 361 Note that this does not change the position of INTERVAL; if it is a root,
362 it is still a root after this operation. */ 362 it is still a root after this operation. */
363 363
364INTERVAL 364INTERVAL
365split_interval_right (interval, offset) 365split_interval_right (interval, offset)
@@ -381,7 +381,7 @@ split_interval_right (interval, offset)
381 return new; 381 return new;
382 } 382 }
383 383
384 /* Insert the new node between INTERVAL and its right child. */ 384 /* Insert the new node between INTERVAL and its right child. */
385 new->right = interval->right; 385 new->right = interval->right;
386 interval->right->parent = new; 386 interval->right->parent = new;
387 interval->right = new; 387 interval->right = new;
@@ -402,7 +402,7 @@ split_interval_right (interval, offset)
402 result. 402 result.
403 403
404 Note that this does not change the position of INTERVAL; if it is a root, 404 Note that this does not change the position of INTERVAL; if it is a root,
405 it is still a root after this operation. */ 405 it is still a root after this operation. */
406 406
407INTERVAL 407INTERVAL
408split_interval_left (interval, offset) 408split_interval_left (interval, offset)
@@ -425,7 +425,7 @@ split_interval_left (interval, offset)
425 return new; 425 return new;
426 } 426 }
427 427
428 /* Insert the new node between INTERVAL and its left child. */ 428 /* Insert the new node between INTERVAL and its left child. */
429 new->left = interval->left; 429 new->left = interval->left;
430 new->left->parent = new; 430 new->left->parent = new;
431 interval->left = new; 431 interval->left = new;
@@ -441,7 +441,7 @@ split_interval_left (interval, offset)
441 441
442 The `position' field, which is a cache of an interval's position, 442 The `position' field, which is a cache of an interval's position,
443 is updated in the interval found. Other functions (e.g., next_interval) 443 is updated in the interval found. Other functions (e.g., next_interval)
444 will update this cache based on the result of find_interval. */ 444 will update this cache based on the result of find_interval. */
445 445
446INLINE INTERVAL 446INLINE INTERVAL
447find_interval (tree, position) 447find_interval (tree, position)
@@ -485,7 +485,7 @@ find_interval (tree, position)
485 485
486/* Find the succeeding interval (lexicographically) to INTERVAL. 486/* Find the succeeding interval (lexicographically) to INTERVAL.
487 Sets the `position' field based on that of INTERVAL (see 487 Sets the `position' field based on that of INTERVAL (see
488 find_interval). */ 488 find_interval). */
489 489
490INTERVAL 490INTERVAL
491next_interval (interval) 491next_interval (interval)
@@ -525,7 +525,7 @@ next_interval (interval)
525 525
526/* Find the preceding interval (lexicographically) to INTERVAL. 526/* Find the preceding interval (lexicographically) to INTERVAL.
527 Sets the `position' field based on that of INTERVAL (see 527 Sets the `position' field based on that of INTERVAL (see
528 find_interval). */ 528 find_interval). */
529 529
530INTERVAL 530INTERVAL
531previous_interval (interval) 531previous_interval (interval)
@@ -574,7 +574,7 @@ previous_interval (interval)
574 finding the interval at position (don't add length going down), 574 finding the interval at position (don't add length going down),
575 if it's the beginning of the interval, get the previous interval 575 if it's the beginning of the interval, get the previous interval
576 and check the hugry bits of both. Then add the length going back up 576 and check the hugry bits of both. Then add the length going back up
577 to the root. */ 577 to the root. */
578 578
579static INTERVAL 579static INTERVAL
580adjust_intervals_for_insertion (tree, position, length) 580adjust_intervals_for_insertion (tree, position, length)
@@ -612,7 +612,7 @@ adjust_intervals_for_insertion (tree, position, length)
612 else 612 else
613 { 613 {
614 /* If we are to use zero-length intervals as buffer pointers, 614 /* If we are to use zero-length intervals as buffer pointers,
615 then this code will have to change. */ 615 then this code will have to change. */
616 this->total_length += length; 616 this->total_length += length;
617 this->position = LEFT_TOTAL_LENGTH (this) 617 this->position = LEFT_TOTAL_LENGTH (this)
618 + position - relative_position + 1; 618 + position - relative_position + 1;
@@ -633,7 +633,7 @@ adjust_intervals_for_insertion (tree, position, length)
633 633
634 If both intervals are "sticky", then make them belong to the left-most 634 If both intervals are "sticky", then make them belong to the left-most
635 interval. Another possibility would be to create a new interval for 635 interval. Another possibility would be to create a new interval for
636 this text, and make it have the merged properties of both ends. */ 636 this text, and make it have the merged properties of both ends. */
637 637
638static INTERVAL 638static INTERVAL
639adjust_intervals_for_insertion (tree, position, length) 639adjust_intervals_for_insertion (tree, position, length)
@@ -641,42 +641,179 @@ adjust_intervals_for_insertion (tree, position, length)
641 int position, length; 641 int position, length;
642{ 642{
643 register INTERVAL i; 643 register INTERVAL i;
644 644 register INTERVAL temp;
645 int eobp = 0;
646
645 if (TOTAL_LENGTH (tree) == 0) /* Paranoia */ 647 if (TOTAL_LENGTH (tree) == 0) /* Paranoia */
646 abort (); 648 abort ();
647 649
648 /* If inserting at point-max of a buffer, that position will be out 650 /* If inserting at point-max of a buffer, that position will be out
649 of range. Remember that buffer positions are 1-based. */ 651 of range. Remember that buffer positions are 1-based. */
650 if (position > BEG + TOTAL_LENGTH (tree)) 652 if (position >= BEG + TOTAL_LENGTH (tree)){
651 position = BEG + TOTAL_LENGTH (tree); 653 position = BEG + TOTAL_LENGTH (tree);
654 eobp = 1;
655 }
652 656
653 i = find_interval (tree, position); 657 i = find_interval (tree, position);
658
654 /* If we are positioned between intervals, check the stickiness of 659 /* If we are positioned between intervals, check the stickiness of
655 both of them. */ 660 both of them. We have to do this too, if we are at BEG or Z. */
656 if (position == i->position 661 if (position == i->position || eobp)
657 && position != BEG)
658 { 662 {
659 register INTERVAL prev = previous_interval (i); 663 register INTERVAL prev;
664
665 if (position == BEG)
666 prev = 0;
667 else if (eobp)
668 {
669 prev = i;
670 i = 0;
671 }
672 else
673 prev = previous_interval (i);
660 674
661 /* If both intervals are sticky here, then default to the 675 /* Even if we are positioned between intervals, we default
662 left-most one. But perhaps we should create a new 676 to the left one if it exists. We extend it now and split
663 interval here instead... */ 677 off a part later, if stickyness demands it. */
664 if (END_STICKY_P (prev) || ! FRONT_STICKY_P (i)) 678 for (temp = prev ? prev : i; ! NULL_INTERVAL_P (temp); temp = temp->parent)
665 i = prev; 679 temp->total_length += length;
680
681 /* If at least one interval has sticky properties,
682 we check the stickyness property by property. */
683 if (END_NONSTICKY_P (prev) || FRONT_STICKY_P (i))
684 {
685 Lisp_Object pleft = NULL_INTERVAL_P (prev) ? Qnil : prev->plist;
686 Lisp_Object pright = NULL_INTERVAL_P (i) ? Qnil : i->plist;
687 struct interval newi;
688
689 newi.plist = merge_properties_sticky (pleft, pright);
690
691 if(! prev) /* i.e. position == BEG */
692 {
693 if (! intervals_equal (i, &newi))
694 {
695 i = split_interval_left (i, length);
696 i->plist = newi.plist;
697 }
698 }
699 else if (! intervals_equal (prev, &newi))
700 {
701 prev = split_interval_right (prev,
702 position - prev->position);
703 prev->plist = newi.plist;
704 if (! NULL_INTERVAL_P (i)
705 && intervals_equal (prev, i))
706 merge_interval_right (prev);
707 }
708
709 /* We will need to update the cache here later. */
710 }
711 else if (! prev && ! NILP (i->plist))
712 {
713 /* Just split off a new interval at the left.
714 Since I wasn't front-sticky, the empty plist is ok. */
715 i = split_interval_left (i, length);
716 }
666 } 717 }
667 718
668 while (! NULL_INTERVAL_P (i)) 719 /* Otherwise just extend the interval. */
720 else
669 { 721 {
670 i->total_length += length; 722 for (temp = i; ! NULL_INTERVAL_P (temp); temp = temp->parent)
671 i = i->parent; 723 temp->total_length += length;
672 } 724 }
673 725
674 return tree; 726 return tree;
675} 727}
728
729Lisp_Object
730merge_properties_sticky (pleft, pright)
731 Lisp_Object pleft, pright;
732{
733 register Lisp_Object props = Qnil, front = Qnil, rear = Qnil;
734
735 Lisp_Object lfront = textget (pleft, Qfront_sticky);
736 Lisp_Object lrear = textget (pleft, Qrear_nonsticky);
737 Lisp_Object rfront = textget (pright, Qfront_sticky);
738 Lisp_Object rrear = textget (pright, Qrear_nonsticky);
739
740 register Lisp_Object tail1, tail2, sym;
741
742 /* Go through each element of PLEFT. */
743 for (tail1 = pleft; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
744 {
745 sym = Fcar (tail1);
746
747 /* Sticky properties get special treatment. */
748 if (EQ (sym, Qrear_nonsticky) || EQ (sym, Qfront_sticky))
749 continue;
750
751 if (NILP (Fmemq (sym, lrear)))
752 {
753 /* rear-sticky is dominant, we needn't search in PRIGHT. */
754
755 props = Fcons (sym, Fcons (Fcar (Fcdr (tail1)), props));
756 if (! NILP (Fmemq (sym, lfront)))
757 front = Fcons (sym, front);
758 }
759 else
760 {
761 /* Go through PRIGHT, looking for sym. */
762 for (tail2 = pright; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
763 if (EQ (sym, Fcar (tail2)))
764 {
765
766 if (! NILP (Fmemq (sym, rfront)))
767 {
768 /* Nonsticky at the left and sticky at the right,
769 so take the right one. */
770 props = Fcons (sym, Fcons (Fcar (Fcdr (tail2)), props));
771 front = Fcons (sym, front);
772 if (! NILP (Fmemq (sym, rrear)))
773 rear = Fcons (sym, rear);
774 }
775 break;
776 }
777 }
778 }
779 /* Now let's see what to keep from PRIGHT. */
780 for (tail2 = pright; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
781 {
782 sym = Fcar (tail2);
783
784 /* Sticky properties get special treatment. */
785 if (EQ (sym, Qrear_nonsticky) || EQ (sym, Qfront_sticky))
786 continue;
787
788 /* If it ain't sticky, we don't take it. */
789 if (NILP (Fmemq (sym, rfront)))
790 continue;
791
792 /* If sym is in PLEFT we already got it. */
793 for (tail1 = pleft; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
794 if (EQ (sym, Fcar (tail1)))
795 break;
796
797 if (NILP (tail1))
798 {
799 props = Fcons (sym, Fcons (Fcar (Fcdr (tail2)), props));
800 front = Fcons (sym, front);
801 if (! NILP (Fmemq (sym, rrear)))
802 rear = Fcons (sym, rear);
803 }
804 }
805 if (! NILP (front))
806 props = Fcons (Qfront_sticky, Fcons (front, props));
807 if (! NILP (rear))
808 props = Fcons (Qrear_nonsticky, Fcons (rear, props));
809 return props;
810
811}
812
676 813
677/* Delete an node I from its interval tree by merging its subtrees 814/* Delete an node I from its interval tree by merging its subtrees
678 into one subtree which is then returned. Caller is responsible for 815 into one subtree which is then returned. Caller is responsible for
679 storing the resulting subtree into its parent. */ 816 storing the resulting subtree into its parent. */
680 817
681static INTERVAL 818static INTERVAL
682delete_node (i) 819delete_node (i)
@@ -709,7 +846,7 @@ delete_node (i)
709 and properly connecting the resultant subtree. 846 and properly connecting the resultant subtree.
710 847
711 I is presumed to be empty; that is, no adjustments are made 848 I is presumed to be empty; that is, no adjustments are made
712 for the length of I. */ 849 for the length of I. */
713 850
714void 851void
715delete_interval (i) 852delete_interval (i)
@@ -718,7 +855,7 @@ delete_interval (i)
718 register INTERVAL parent; 855 register INTERVAL parent;
719 int amt = LENGTH (i); 856 int amt = LENGTH (i);
720 857
721 if (amt > 0) /* Only used on zero-length intervals now. */ 858 if (amt > 0) /* Only used on zero-length intervals now. */
722 abort (); 859 abort ();
723 860
724 if (ROOT_INTERVAL_P (i)) 861 if (ROOT_INTERVAL_P (i))
@@ -763,7 +900,7 @@ delete_interval (i)
763 recursively on subtrees. 900 recursively on subtrees.
764 901
765 Do this by recursing down TREE to the interval in question, and 902 Do this by recursing down TREE to the interval in question, and
766 deleting the appropriate amount of text. */ 903 deleting the appropriate amount of text. */
767 904
768static int 905static int
769interval_deletion_adjustment (tree, from, amount) 906interval_deletion_adjustment (tree, from, amount)
@@ -798,7 +935,7 @@ interval_deletion_adjustment (tree, from, amount)
798 tree->total_length -= subtract; 935 tree->total_length -= subtract;
799 return subtract; 936 return subtract;
800 } 937 }
801 /* Here -- this node */ 938 /* Here -- this node. */
802 else 939 else
803 { 940 {
804 /* How much can we delete from this interval? */ 941 /* How much can we delete from this interval? */
@@ -816,13 +953,13 @@ interval_deletion_adjustment (tree, from, amount)
816 return amount; 953 return amount;
817 } 954 }
818 955
819 /* Never reach here */ 956 /* Never reach here. */
820} 957}
821 958
822/* Effect the adjustments necessary to the interval tree of BUFFER to 959/* Effect the adjustments necessary to the interval tree of BUFFER to
823 correspond to the deletion of LENGTH characters from that buffer 960 correspond to the deletion of LENGTH characters from that buffer
824 text. The deletion is effected at position START (which is a 961 text. The deletion is effected at position START (which is a
825 buffer position, i.e. origin 1). */ 962 buffer position, i.e. origin 1). */
826 963
827static void 964static void
828adjust_intervals_for_deletion (buffer, start, length) 965adjust_intervals_for_deletion (buffer, start, length)
@@ -870,7 +1007,7 @@ adjust_intervals_for_deletion (buffer, start, length)
870/* Make the adjustments necessary to the interval tree of BUFFER to 1007/* Make the adjustments necessary to the interval tree of BUFFER to
871 represent an addition or deletion of LENGTH characters starting 1008 represent an addition or deletion of LENGTH characters starting
872 at position START. Addition or deletion is indicated by the sign 1009 at position START. Addition or deletion is indicated by the sign
873 of LENGTH. */ 1010 of LENGTH. */
874 1011
875INLINE void 1012INLINE void
876offset_intervals (buffer, start, length) 1013offset_intervals (buffer, start, length)
@@ -893,7 +1030,7 @@ offset_intervals (buffer, start, length)
893 1030
894 IMPORTANT: 1031 IMPORTANT:
895 The caller must verify that this is not the last (rightmost) 1032 The caller must verify that this is not the last (rightmost)
896 interval. */ 1033 interval. */
897 1034
898INTERVAL 1035INTERVAL
899merge_interval_right (i) 1036merge_interval_right (i)
@@ -902,12 +1039,12 @@ merge_interval_right (i)
902 register int absorb = LENGTH (i); 1039 register int absorb = LENGTH (i);
903 register INTERVAL successor; 1040 register INTERVAL successor;
904 1041
905 /* Zero out this interval. */ 1042 /* Zero out this interval. */
906 i->total_length -= absorb; 1043 i->total_length -= absorb;
907 1044
908 /* Find the succeeding interval. */ 1045 /* Find the succeeding interval. */
909 if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb 1046 if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb
910 as we descend. */ 1047 as we descend. */
911 { 1048 {
912 successor = i->right; 1049 successor = i->right;
913 while (! NULL_LEFT_CHILD (successor)) 1050 while (! NULL_LEFT_CHILD (successor))
@@ -923,7 +1060,7 @@ merge_interval_right (i)
923 1060
924 successor = i; 1061 successor = i;
925 while (! NULL_PARENT (successor)) /* It's above us. Subtract as 1062 while (! NULL_PARENT (successor)) /* It's above us. Subtract as
926 we ascend. */ 1063 we ascend. */
927 { 1064 {
928 if (AM_LEFT_CHILD (successor)) 1065 if (AM_LEFT_CHILD (successor))
929 { 1066 {
@@ -937,7 +1074,7 @@ merge_interval_right (i)
937 } 1074 }
938 1075
939 /* This must be the rightmost or last interval and cannot 1076 /* This must be the rightmost or last interval and cannot
940 be merged right. The caller should have known. */ 1077 be merged right. The caller should have known. */
941 abort (); 1078 abort ();
942} 1079}
943 1080
@@ -946,7 +1083,7 @@ merge_interval_right (i)
946 The properties of I are lost. Interval node I is removed from the tree. 1083 The properties of I are lost. Interval node I is removed from the tree.
947 1084
948 IMPORTANT: 1085 IMPORTANT:
949 The caller must verify that this is not the first (leftmost) interval. */ 1086 The caller must verify that this is not the first (leftmost) interval. */
950 1087
951INTERVAL 1088INTERVAL
952merge_interval_left (i) 1089merge_interval_left (i)
@@ -955,12 +1092,12 @@ merge_interval_left (i)
955 register int absorb = LENGTH (i); 1092 register int absorb = LENGTH (i);
956 register INTERVAL predecessor; 1093 register INTERVAL predecessor;
957 1094
958 /* Zero out this interval. */ 1095 /* Zero out this interval. */
959 i->total_length -= absorb; 1096 i->total_length -= absorb;
960 1097
961 /* Find the preceding interval. */ 1098 /* Find the preceding interval. */
962 if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down, 1099 if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down,
963 adding ABSORB as we go. */ 1100 adding ABSORB as we go. */
964 { 1101 {
965 predecessor = i->left; 1102 predecessor = i->left;
966 while (! NULL_RIGHT_CHILD (predecessor)) 1103 while (! NULL_RIGHT_CHILD (predecessor))
@@ -976,7 +1113,7 @@ merge_interval_left (i)
976 1113
977 predecessor = i; 1114 predecessor = i;
978 while (! NULL_PARENT (predecessor)) /* It's above us. Go up, 1115 while (! NULL_PARENT (predecessor)) /* It's above us. Go up,
979 subtracting ABSORB. */ 1116 subtracting ABSORB. */
980 { 1117 {
981 if (AM_RIGHT_CHILD (predecessor)) 1118 if (AM_RIGHT_CHILD (predecessor))
982 { 1119 {
@@ -990,14 +1127,14 @@ merge_interval_left (i)
990 } 1127 }
991 1128
992 /* This must be the leftmost or first interval and cannot 1129 /* This must be the leftmost or first interval and cannot
993 be merged left. The caller should have known. */ 1130 be merged left. The caller should have known. */
994 abort (); 1131 abort ();
995} 1132}
996 1133
997/* Make an exact copy of interval tree SOURCE which descends from 1134/* Make an exact copy of interval tree SOURCE which descends from
998 PARENT. This is done by recursing through SOURCE, copying 1135 PARENT. This is done by recursing through SOURCE, copying
999 the current interval and its properties, and then adjusting 1136 the current interval and its properties, and then adjusting
1000 the pointers of the copy. */ 1137 the pointers of the copy. */
1001 1138
1002static INTERVAL 1139static INTERVAL
1003reproduce_tree (source, parent) 1140reproduce_tree (source, parent)
@@ -1024,7 +1161,7 @@ reproduce_tree (source, parent)
1024 Returns the new interval. 1161 Returns the new interval.
1025 1162
1026 Generate an error if the new positions would overlap an existing 1163 Generate an error if the new positions would overlap an existing
1027 interval. */ 1164 interval. */
1028 1165
1029static INTERVAL 1166static INTERVAL
1030make_new_interval (intervals, start, length) 1167make_new_interval (intervals, start, length)
@@ -1042,19 +1179,19 @@ make_new_interval (intervals, start, length)
1042 1179
1043 if (slot->position == start) 1180 if (slot->position == start)
1044 { 1181 {
1045 /* New right node. */ 1182 /* New right node. */
1046 split_interval_right (slot, length); 1183 split_interval_right (slot, length);
1047 return slot; 1184 return slot;
1048 } 1185 }
1049 1186
1050 if (slot->position + LENGTH (slot) == start + length) 1187 if (slot->position + LENGTH (slot) == start + length)
1051 { 1188 {
1052 /* New left node. */ 1189 /* New left node. */
1053 split_interval_left (slot, LENGTH (slot) - length); 1190 split_interval_left (slot, LENGTH (slot) - length);
1054 return slot; 1191 return slot;
1055 } 1192 }
1056 1193
1057 /* Convert interval SLOT into three intervals. */ 1194 /* Convert interval SLOT into three intervals. */
1058 split_interval_left (slot, start - slot->position); 1195 split_interval_left (slot, start - slot->position);
1059 split_interval_right (slot, length); 1196 split_interval_right (slot, length);
1060 return slot; 1197 return slot;
@@ -1091,7 +1228,7 @@ make_new_interval (intervals, start, length)
1091 intervals to the new text are "sticky", then the new text retains 1228 intervals to the new text are "sticky", then the new text retains
1092 only its properties, as if neither sticky property were set. Perhaps 1229 only its properties, as if neither sticky property were set. Perhaps
1093 we should consider merging all three sets of properties onto the new 1230 we should consider merging all three sets of properties onto the new
1094 text... */ 1231 text... */
1095 1232
1096void 1233void
1097graft_intervals_into_buffer (source, position, buffer) 1234graft_intervals_into_buffer (source, position, buffer)
@@ -1104,26 +1241,26 @@ graft_intervals_into_buffer (source, position, buffer)
1104 int middle; 1241 int middle;
1105 1242
1106 /* If the new text has no properties, it becomes part of whatever 1243 /* If the new text has no properties, it becomes part of whatever
1107 interval it was inserted into. */ 1244 interval it was inserted into. */
1108 if (NULL_INTERVAL_P (source)) 1245 if (NULL_INTERVAL_P (source))
1109 return; 1246 return;
1110 1247
1111 if (NULL_INTERVAL_P (tree)) 1248 if (NULL_INTERVAL_P (tree))
1112 { 1249 {
1113 /* The inserted text constitutes the whole buffer, so 1250 /* The inserted text constitutes the whole buffer, so
1114 simply copy over the interval structure. */ 1251 simply copy over the interval structure. */
1115 if ((BUF_Z (buffer) - BUF_BEG (buffer)) == TOTAL_LENGTH (source)) 1252 if ((BUF_Z (buffer) - BUF_BEG (buffer)) == TOTAL_LENGTH (source))
1116 { 1253 {
1117 Lisp_Object buf; 1254 Lisp_Object buf;
1118 XSET (buf, Lisp_Buffer, buffer); 1255 XSET (buf, Lisp_Buffer, buffer);
1119 buffer->intervals = reproduce_tree (source, buf); 1256 buffer->intervals = reproduce_tree (source, buf);
1120 /* Explicitly free the old tree here. */ 1257 /* Explicitly free the old tree here. */
1121 1258
1122 return; 1259 return;
1123 } 1260 }
1124 1261
1125 /* Create an interval tree in which to place a copy 1262 /* Create an interval tree in which to place a copy
1126 of the intervals of the inserted string. */ 1263 of the intervals of the inserted string. */
1127 { 1264 {
1128 Lisp_Object buf; 1265 Lisp_Object buf;
1129 XSET (buf, Lisp_Buffer, buffer); 1266 XSET (buf, Lisp_Buffer, buffer);
@@ -1135,16 +1272,16 @@ graft_intervals_into_buffer (source, position, buffer)
1135 /* If the buffer contains only the new string, but 1272 /* If the buffer contains only the new string, but
1136 there was already some interval tree there, then it may be 1273 there was already some interval tree there, then it may be
1137 some zero length intervals. Eventually, do something clever 1274 some zero length intervals. Eventually, do something clever
1138 about inserting properly. For now, just waste the old intervals. */ 1275 about inserting properly. For now, just waste the old intervals. */
1139 { 1276 {
1140 buffer->intervals = reproduce_tree (source, tree->parent); 1277 buffer->intervals = reproduce_tree (source, tree->parent);
1141 /* Explicitly free the old tree here. */ 1278 /* Explicitly free the old tree here. */
1142 1279
1143 return; 1280 return;
1144 } 1281 }
1145 else 1282 else
1146 /* Paranoia -- the text has already been added, so this buffer 1283 /* Paranoia -- the text has already been added, so this buffer
1147 should be of non-zero length. */ 1284 should be of non-zero length. */
1148 if (TOTAL_LENGTH (tree) == 0) 1285 if (TOTAL_LENGTH (tree) == 0)
1149 abort (); 1286 abort ();
1150 1287
@@ -1169,31 +1306,32 @@ graft_intervals_into_buffer (source, position, buffer)
1169 else 1306 else
1170 { 1307 {
1171 prev = previous_interval (under); 1308 prev = previous_interval (under);
1172 if (prev && !END_STICKY_P (prev)) 1309 if (prev && !END_NONSTICKY_P (prev))
1173 prev = 0; 1310 prev = 0;
1174 } 1311 }
1175 1312
1176 /* Insertion is now at beginning of UNDER. */ 1313 /* Insertion is now at beginning of UNDER. */
1177 1314
1178 /* The inserted text "sticks" to the interval `under', 1315 /* The inserted text "sticks" to the interval `under',
1179 which means it gets those properties. */ 1316 which means it gets those properties.
1317 The properties of under are the result of
1318 adjust_intervals_for_insertion, so stickyness has
1319 already been taken care of. */
1320
1180 while (! NULL_INTERVAL_P (over)) 1321 while (! NULL_INTERVAL_P (over))
1181 { 1322 {
1182 if (LENGTH (over) + 1 < LENGTH (under)) 1323 if (LENGTH (over) + 1 < LENGTH (under))
1183 this = split_interval_left (under, LENGTH (over)); 1324 {
1325 this = split_interval_left (under, LENGTH (over));
1326 copy_properties (under, this);
1327 }
1184 else 1328 else
1185 this = under; 1329 this = under;
1186 copy_properties (over, this); 1330 copy_properties (over, this);
1187 /* Insertion at the end of an interval, PREV, 1331 if (MERGE_INSERTIONS (this))
1188 inherits from PREV if PREV is sticky at the end. */ 1332 merge_properties (over, this);
1189 if (prev && ! FRONT_STICKY_P (under) 1333 else
1190 && MERGE_INSERTIONS (prev)) 1334 copy_properties (over, this);
1191 merge_properties (prev, this);
1192 /* Maybe it inherits from the following interval
1193 if that is sticky at the front. */
1194 else if ((FRONT_STICKY_P (under) || middle)
1195 && MERGE_INSERTIONS (under))
1196 merge_properties (under, this);
1197 over = next_interval (over); 1335 over = next_interval (over);
1198 } 1336 }
1199 1337
@@ -1225,6 +1363,26 @@ textget (plist, prop)
1225 1363
1226 return fallback; 1364 return fallback;
1227} 1365}
1366
1367/* Get the value of property PROP from PLIST,
1368 which is the plist of an interval.
1369 We check for direct properties only! */
1370
1371Lisp_Object
1372textget_direct (plist, prop)
1373 Lisp_Object plist;
1374 register Lisp_Object prop;
1375{
1376 register Lisp_Object tail;
1377
1378 for (tail = plist; !NILP (tail); tail = Fcdr (Fcdr (tail)))
1379 {
1380 if (EQ (prop, Fcar (tail)))
1381 return Fcar (Fcdr (tail));
1382 }
1383
1384 return Qnil;
1385}
1228 1386
1229/* Set point in BUFFER to POSITION. If the target position is 1387/* Set point in BUFFER to POSITION. If the target position is
1230 before an invisible character which is not displayed with a special glyph, 1388 before an invisible character which is not displayed with a special glyph,
@@ -1274,9 +1432,9 @@ set_point (position, buffer)
1274 /* Set FROM to the interval containing the char after PT, 1432 /* Set FROM to the interval containing the char after PT,
1275 and FROMPREV to the interval containing the char before PT. 1433 and FROMPREV to the interval containing the char before PT.
1276 Either one may be null. They may be equal. */ 1434 Either one may be null. They may be equal. */
1277 /* We could cache this and save time. */ 1435 /* We could cache this and save time. */
1278 from = find_interval (buffer->intervals, buffer_point); 1436 from = find_interval (buffer->intervals, buffer_point);
1279 if (from->position == BUF_BEGV (buffer)) 1437 if (buffer_point == BUF_BEGV (buffer))
1280 fromprev = 0; 1438 fromprev = 0;
1281 else if (from->position == BUF_PT (buffer)) 1439 else if (from->position == BUF_PT (buffer))
1282 fromprev = previous_interval (from); 1440 fromprev = previous_interval (from);
@@ -1285,17 +1443,18 @@ set_point (position, buffer)
1285 else 1443 else
1286 fromprev = from; 1444 fromprev = from;
1287 1445
1288 /* Moving within an interval */ 1446 /* Moving within an interval. */
1289 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to)) 1447 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to))
1290 { 1448 {
1291 buffer->text.pt = position; 1449 buffer->text.pt = position;
1292 return; 1450 return;
1293 } 1451 }
1294 1452
1295 /* If the new position is before an invisible character, 1453 /* If the new position is before an invisible character
1454 that has an `invisible' property of value `hidden',
1296 move forward over all such. */ 1455 move forward over all such. */
1297 while (! NULL_INTERVAL_P (to) 1456 while (! NULL_INTERVAL_P (to)
1298 && ! INTERVAL_VISIBLE_P (to) 1457 && EQ (textget (to->plist, Qinvisible), Qhidden)
1299 && ! DISPLAY_INVISIBLE_GLYPH (to)) 1458 && ! DISPLAY_INVISIBLE_GLYPH (to))
1300 { 1459 {
1301 toprev = to; 1460 toprev = to;
@@ -1347,7 +1506,7 @@ set_point (position, buffer)
1347 } 1506 }
1348} 1507}
1349 1508
1350/* Set point temporarily, without checking any text properties. */ 1509/* Set point temporarily, without checking any text properties. */
1351 1510
1352INLINE void 1511INLINE void
1353temp_set_point (position, buffer) 1512temp_set_point (position, buffer)
@@ -1372,7 +1531,7 @@ get_local_map (position, buffer)
1372 if (NULL_INTERVAL_P (buffer->intervals)) 1531 if (NULL_INTERVAL_P (buffer->intervals))
1373 return current_buffer->keymap; 1532 return current_buffer->keymap;
1374 1533
1375 /* Perhaps we should just change `position' to the limit. */ 1534 /* Perhaps we should just change `position' to the limit. */
1376 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer)) 1535 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer))
1377 abort (); 1536 abort ();
1378 1537
@@ -1410,7 +1569,7 @@ call_mod_hooks (list, start, end)
1410 (but not including) TO. Create a list of all these hooks in 1569 (but not including) TO. Create a list of all these hooks in
1411 lexicographic order, eliminating consecutive extra copies of the 1570 lexicographic order, eliminating consecutive extra copies of the
1412 same hook. Then call those hooks in order, with START and END - 1 1571 same hook. Then call those hooks in order, with START and END - 1
1413 as arguments. */ 1572 as arguments. */
1414 1573
1415void 1574void
1416verify_interval_modification (buf, start, end) 1575verify_interval_modification (buf, start, end)
@@ -1451,32 +1610,73 @@ verify_interval_modification (buf, start, end)
1451 1610
1452 if (start == BUF_BEGV (buf)) 1611 if (start == BUF_BEGV (buf))
1453 prev = 0; 1612 prev = 0;
1454 if (i->position == start) 1613 else if (i->position == start)
1455 prev = previous_interval (i); 1614 prev = previous_interval (i);
1456 else if (i->position < start) 1615 else if (i->position < start)
1457 prev = i; 1616 prev = i;
1458 if (start == BUF_ZV (buf)) 1617 if (start == BUF_ZV (buf))
1459 i = 0; 1618 i = 0;
1460 1619
1461 if (NULL_INTERVAL_P (prev)) 1620 /* If Vinhibit_read_only is set and is not a list, we can
1462 { 1621 skip the read_only checks. */
1463 if (! INTERVAL_WRITABLE_P (i)) 1622 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
1464 error ("Attempt to insert within read-only text");
1465 }
1466 else if (NULL_INTERVAL_P (i))
1467 {
1468 if (! INTERVAL_WRITABLE_P (prev))
1469 error ("Attempt to insert within read-only text");
1470 }
1471 else
1472 { 1623 {
1473 before = textget (prev->plist, Qread_only); 1624 /* If I and PREV differ we need to check for the read-only
1474 after = textget (i->plist, Qread_only); 1625 property together with its stickyness. If either I or
1475 if (! NILP (before) && EQ (before, after) 1626 PREV are 0, this check is all we need.
1476 /* This checks Vinhibit_read_only properly 1627 We have to take special care, since read-only may be
1477 for the common value of the read-only property. */ 1628 indirectly defined via the category property. */
1478 && ! INTERVAL_WRITABLE_P (i)) 1629 if (i != prev)
1479 error ("Attempt to insert within read-only text"); 1630 {
1631 if (! NULL_INTERVAL_P (i))
1632 {
1633 after = textget (i->plist, Qread_only);
1634
1635 /* If interval I is read-only and read-only is
1636 front-sticky, inhibit insertion.
1637 Check for read-only as well as category. */
1638 if (! NILP (after)
1639 && NILP (Fmemq (after, Vinhibit_read_only))
1640 && (! NILP (Fmemq (Qread_only,
1641 textget (i->plist, Qfront_sticky)))
1642 || (NILP (textget_direct (i->plist, Qread_only))
1643 && ! NILP (Fmemq (Qcategory,
1644 textget (i->plist,
1645 Qfront_sticky))))))
1646 error ("Attempt to insert within read-only text");
1647 }
1648 else
1649 after = Qnil;
1650 if (! NULL_INTERVAL_P (prev))
1651 {
1652 before = textget (prev->plist, Qread_only);
1653
1654 /* If interval PREV is read-only and read-only isn't
1655 rear-nonsticky, inhibit insertion.
1656 Check for read-only as well as category. */
1657 if (! NILP (before)
1658 && NILP (Fmemq (before, Vinhibit_read_only))
1659 && NILP (Fmemq (Qread_only,
1660 textget (prev->plist, Qrear_nonsticky)))
1661 && (! NILP (textget_direct (prev->plist,Qread_only))
1662 || NILP (Fmemq (Qcategory,
1663 textget (prev->plist,
1664 Qrear_nonsticky)))))
1665 error ("Attempt to insert within read-only text");
1666 }
1667 else
1668 before = Qnil;
1669 }
1670 else if (! NULL_INTERVAL_P (i))
1671 before = after = textget (i->plist, Qread_only);
1672 if (! NULL_INTERVAL_P (i) && ! NULL_INTERVAL_P (prev))
1673 {
1674 /* If I and PREV differ, neither of them has a sticky
1675 read-only property. It only remains to check, whether
1676 they have a common read-only property. */
1677 if (! NILP (before) && EQ (before, after))
1678 error ("Attempt to insert within read-only text");
1679 }
1480 } 1680 }
1481 1681
1482 /* Run both insert hooks (just once if they're the same). */ 1682 /* Run both insert hooks (just once if they're the same). */
@@ -1529,7 +1729,7 @@ verify_interval_modification (buf, start, end)
1529 1729
1530/* Balance an interval node if the amount of text in its left and right 1730/* Balance an interval node if the amount of text in its left and right
1531 subtrees differs by more than the percentage specified by 1731 subtrees differs by more than the percentage specified by
1532 `interval-balance-threshold'. */ 1732 `interval-balance-threshold'. */
1533 1733
1534static INTERVAL 1734static INTERVAL
1535balance_an_interval (i) 1735balance_an_interval (i)
@@ -1569,7 +1769,7 @@ balance_an_interval (i)
1569} 1769}
1570 1770
1571/* Balance the interval tree TREE. Balancing is by weight 1771/* Balance the interval tree TREE. Balancing is by weight
1572 (the amount of text). */ 1772 (the amount of text). */
1573 1773
1574INTERVAL 1774INTERVAL
1575balance_intervals (tree) 1775balance_intervals (tree)
@@ -1592,7 +1792,7 @@ balance_intervals (tree)
1592} 1792}
1593 1793
1594/* Produce an interval tree reflecting the intervals in 1794/* Produce an interval tree reflecting the intervals in
1595 TREE from START to START + LENGTH. */ 1795 TREE from START to START + LENGTH. */
1596 1796
1597INTERVAL 1797INTERVAL
1598copy_intervals (tree, start, length) 1798copy_intervals (tree, start, length)
@@ -1609,7 +1809,7 @@ copy_intervals (tree, start, length)
1609 if (NULL_INTERVAL_P (i) || LENGTH (i) == 0) 1809 if (NULL_INTERVAL_P (i) || LENGTH (i) == 0)
1610 abort (); 1810 abort ();
1611 1811
1612 /* If there is only one interval and it's the default, return nil. */ 1812 /* If there is only one interval and it's the default, return nil. */
1613 if ((start - i->position + 1 + length) < LENGTH (i) 1813 if ((start - i->position + 1 + length) < LENGTH (i)
1614 && DEFAULT_INTERVAL_P (i)) 1814 && DEFAULT_INTERVAL_P (i))
1615 return NULL_INTERVAL; 1815 return NULL_INTERVAL;
@@ -1634,7 +1834,7 @@ copy_intervals (tree, start, length)
1634 return balance_intervals (new); 1834 return balance_intervals (new);
1635} 1835}
1636 1836
1637/* Give STRING the properties of BUFFER from POSITION to LENGTH. */ 1837/* Give STRING the properties of BUFFER from POSITION to LENGTH. */
1638 1838
1639INLINE void 1839INLINE void
1640copy_intervals_to_string (string, buffer, position, length) 1840copy_intervals_to_string (string, buffer, position, length)