(parens-dont-require-spaces): New variable.
[bpt/emacs.git] / src / intervals.c
CommitLineData
a50699fd 1/* Code for doing intervals.
294efdbe 2 Copyright (C) 1993 Free Software Foundation, Inc.
a50699fd
JA
3
4This file is part of GNU Emacs.
5
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
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21/* NOTES:
22
23 Have to ensure that we can't put symbol nil on a plist, or some
24 functions may work incorrectly.
25
26 An idea: Have the owner of the tree keep count of splits and/or
27 insertion lengths (in intervals), and balance after every N.
28
29 Need to call *_left_hook when buffer is killed.
30
31 Scan for zero-length, or 0-length to see notes about handling
32 zero length interval-markers.
33
34 There are comments around about freeing intervals. It might be
35 faster to explicitly free them (put them on the free list) than
36 to GC them.
37
38*/
39
40
41#include "config.h"
42#include "lisp.h"
43#include "intervals.h"
44#include "buffer.h"
a50699fd 45
d2f7a802
JA
46/* The rest of the file is within this conditional. */
47#ifdef USE_TEXT_PROPERTIES
48
a50699fd
JA
49/* Factor for weight-balancing interval trees. */
50Lisp_Object interval_balance_threshold;
51\f
52/* Utility functions for intervals. */
53
54
55/* Create the root interval of some object, a buffer or string. */
56
57INTERVAL
58create_root_interval (parent)
59 Lisp_Object parent;
60{
61 INTERVAL new = make_interval ();
62
63 if (XTYPE (parent) == Lisp_Buffer)
64 {
65 new->total_length = BUF_Z (XBUFFER (parent)) - 1;
66 XBUFFER (parent)->intervals = new;
67 }
68 else if (XTYPE (parent) == Lisp_String)
69 {
70 new->total_length = XSTRING (parent)->size;
71 XSTRING (parent)->intervals = new;
72 }
73
74 new->parent = (INTERVAL) parent;
75 new->position = 1;
76
77 return new;
78}
79
80/* Make the interval TARGET have exactly the properties of SOURCE */
81
82void
83copy_properties (source, target)
84 register INTERVAL source, target;
85{
86 if (DEFAULT_INTERVAL_P (source) && DEFAULT_INTERVAL_P (target))
87 return;
88
89 COPY_INTERVAL_CACHE (source, target);
90 target->plist = Fcopy_sequence (source->plist);
91}
92
93/* Merge the properties of interval SOURCE into the properties
323a7ad4
RS
94 of interval TARGET. That is to say, each property in SOURCE
95 is added to TARGET if TARGET has no such property as yet. */
a50699fd
JA
96
97static void
98merge_properties (source, target)
99 register INTERVAL source, target;
100{
101 register Lisp_Object o, sym, val;
102
103 if (DEFAULT_INTERVAL_P (source) && DEFAULT_INTERVAL_P (target))
104 return;
105
106 MERGE_INTERVAL_CACHE (source, target);
107
108 o = source->plist;
109 while (! EQ (o, Qnil))
110 {
111 sym = Fcar (o);
112 val = Fmemq (sym, target->plist);
113
114 if (NILP (val))
115 {
116 o = Fcdr (o);
117 val = Fcar (o);
118 target->plist = Fcons (sym, Fcons (val, target->plist));
119 o = Fcdr (o);
120 }
121 else
122 o = Fcdr (Fcdr (o));
123 }
124}
125
126/* Return 1 if the two intervals have the same properties,
127 0 otherwise. */
128
129int
130intervals_equal (i0, i1)
131 INTERVAL i0, i1;
132{
133 register Lisp_Object i0_cdr, i0_sym, i1_val;
134 register i1_len;
135
136 if (DEFAULT_INTERVAL_P (i0) && DEFAULT_INTERVAL_P (i1))
137 return 1;
138
323a7ad4
RS
139 if (DEFAULT_INTERVAL_P (i0) || DEFAULT_INTERVAL_P (i1))
140 return 0;
141
a50699fd
JA
142 i1_len = XFASTINT (Flength (i1->plist));
143 if (i1_len & 0x1) /* Paranoia -- plists are always even */
144 abort ();
145 i1_len /= 2;
146 i0_cdr = i0->plist;
147 while (!NILP (i0_cdr))
148 {
149 /* Lengths of the two plists were unequal */
150 if (i1_len == 0)
151 return 0;
152
153 i0_sym = Fcar (i0_cdr);
154 i1_val = Fmemq (i0_sym, i1->plist);
155
156 /* i0 has something i1 doesn't */
157 if (EQ (i1_val, Qnil))
158 return 0;
159
160 /* i0 and i1 both have sym, but it has different values in each */
161 i0_cdr = Fcdr (i0_cdr);
162 if (! Fequal (i1_val, Fcar (i0_cdr)))
163 return 0;
164
165 i0_cdr = Fcdr (i0_cdr);
166 i1_len--;
167 }
168
169 /* Lengths of the two plists were unequal */
170 if (i1_len > 0)
171 return 0;
172
173 return 1;
174}
175\f
176static int icount;
177static int idepth;
178static int zero_length;
179
a50699fd 180/* Traverse an interval tree TREE, performing FUNCTION on each node.
4a93c905 181 Pass FUNCTION two args: an interval, and ARG. */
a50699fd
JA
182
183void
4a93c905 184traverse_intervals (tree, position, depth, function, arg)
a50699fd 185 INTERVAL tree;
e0b63493 186 int position, depth;
a50699fd 187 void (* function) ();
4a93c905 188 Lisp_Object arg;
a50699fd
JA
189{
190 if (NULL_INTERVAL_P (tree))
191 return;
192
323a7ad4 193 traverse_intervals (tree->left, position, depth + 1, function, arg);
a50699fd
JA
194 position += LEFT_TOTAL_LENGTH (tree);
195 tree->position = position;
4a93c905 196 (*function) (tree, arg);
a50699fd 197 position += LENGTH (tree);
323a7ad4 198 traverse_intervals (tree->right, position, depth + 1, function, arg);
a50699fd
JA
199}
200\f
201#if 0
202/* These functions are temporary, for debugging purposes only. */
203
204INTERVAL search_interval, found_interval;
205
206void
207check_for_interval (i)
208 register INTERVAL i;
209{
210 if (i == search_interval)
211 {
212 found_interval = i;
213 icount++;
214 }
215}
216
217INTERVAL
218search_for_interval (i, tree)
219 register INTERVAL i, tree;
220{
221 icount = 0;
222 search_interval = i;
223 found_interval = NULL_INTERVAL;
4a93c905 224 traverse_intervals (tree, 1, 0, &check_for_interval, Qnil);
a50699fd
JA
225 return found_interval;
226}
227
228static void
229inc_interval_count (i)
230 INTERVAL i;
231{
232 icount++;
233 if (LENGTH (i) == 0)
234 zero_length++;
235 if (depth > idepth)
236 idepth = depth;
237}
238
239int
240count_intervals (i)
241 register INTERVAL i;
242{
243 icount = 0;
244 idepth = 0;
245 zero_length = 0;
4a93c905 246 traverse_intervals (i, 1, 0, &inc_interval_count, Qnil);
a50699fd
JA
247
248 return icount;
249}
250
251static INTERVAL
252root_interval (interval)
253 INTERVAL interval;
254{
255 register INTERVAL i = interval;
256
257 while (! ROOT_INTERVAL_P (i))
258 i = i->parent;
259
260 return i;
261}
262#endif
263\f
264/* Assuming that a left child exists, perform the following operation:
265
266 A B
267 / \ / \
268 B => A
269 / \ / \
270 c c
271*/
272
273static INTERVAL
274rotate_right (interval)
275 INTERVAL interval;
276{
277 INTERVAL i;
278 INTERVAL B = interval->left;
279 int len = LENGTH (interval);
280
281 /* Deal with any Parent of A; make it point to B. */
282 if (! ROOT_INTERVAL_P (interval))
283 if (AM_LEFT_CHILD (interval))
284 interval->parent->left = interval->left;
285 else
286 interval->parent->right = interval->left;
287 interval->left->parent = interval->parent;
288
289 /* B gets the same length as A, since it get A's position in the tree. */
290 interval->left->total_length = interval->total_length;
291
292 /* B becomes the parent of A. */
293 i = interval->left->right;
294 interval->left->right = interval;
295 interval->parent = interval->left;
296
297 /* A gets c as left child. */
298 interval->left = i;
299 if (! NULL_INTERVAL_P (i))
300 i->parent = interval;
301 interval->total_length = (len + LEFT_TOTAL_LENGTH (interval)
302 + RIGHT_TOTAL_LENGTH (interval));
303
304 return B;
305}
306\f
307/* Assuming that a right child exists, perform the following operation:
308
309 A B
310 / \ / \
311 B => A
312 / \ / \
313 c c
314*/
315
316static INTERVAL
317rotate_left (interval)
318 INTERVAL interval;
319{
320 INTERVAL i;
321 INTERVAL B = interval->right;
322 int len = LENGTH (interval);
323
324 /* Deal with the parent of A. */
325 if (! ROOT_INTERVAL_P (interval))
326 if (AM_LEFT_CHILD (interval))
327 interval->parent->left = interval->right;
328 else
329 interval->parent->right = interval->right;
330 interval->right->parent = interval->parent;
331
332 /* B must have the same total length of A. */
333 interval->right->total_length = interval->total_length;
334
335 /* Make B the parent of A */
336 i = interval->right->left;
337 interval->right->left = interval;
338 interval->parent = interval->right;
339
340 /* Make A point to c */
341 interval->right = i;
342 if (! NULL_INTERVAL_P (i))
343 i->parent = interval;
344 interval->total_length = (len + LEFT_TOTAL_LENGTH (interval)
345 + RIGHT_TOTAL_LENGTH (interval));
346
347 return B;
348}
349\f
90ba40fc
JA
350/* Split INTERVAL into two pieces, starting the second piece at character
351 position OFFSET (counting from 1), relative to INTERVAL. The right-hand
352 piece (second, lexicographically) is returned.
353
354 The size and position fields of the two intervals are set based upon
355 those of the original interval. The property list of the new interval
356 is reset, thus it is up to the caller to do the right thing with the
357 result.
a50699fd
JA
358
359 Note that this does not change the position of INTERVAL; if it is a root,
360 it is still a root after this operation. */
361
362INTERVAL
90ba40fc 363split_interval_right (interval, offset)
a50699fd 364 INTERVAL interval;
90ba40fc 365 int offset;
a50699fd
JA
366{
367 INTERVAL new = make_interval ();
368 int position = interval->position;
90ba40fc 369 int new_length = LENGTH (interval) - offset + 1;
a50699fd 370
90ba40fc 371 new->position = position + offset - 1;
a50699fd 372 new->parent = interval;
a50699fd
JA
373
374 if (LEAF_INTERVAL_P (interval) || NULL_RIGHT_CHILD (interval))
375 {
376 interval->right = new;
377 new->total_length = new_length;
378
379 return new;
380 }
381
382 /* Insert the new node between INTERVAL and its right child. */
383 new->right = interval->right;
384 interval->right->parent = new;
385 interval->right = new;
386
387 new->total_length = new_length + new->right->total_length;
388
389 return new;
390}
391
90ba40fc
JA
392/* Split INTERVAL into two pieces, starting the second piece at character
393 position OFFSET (counting from 1), relative to INTERVAL. The left-hand
394 piece (first, lexicographically) is returned.
a50699fd 395
90ba40fc
JA
396 The size and position fields of the two intervals are set based upon
397 those of the original interval. The property list of the new interval
398 is reset, thus it is up to the caller to do the right thing with the
399 result.
400
401 Note that this does not change the position of INTERVAL; if it is a root,
402 it is still a root after this operation. */
a50699fd
JA
403
404INTERVAL
90ba40fc 405split_interval_left (interval, offset)
a50699fd 406 INTERVAL interval;
90ba40fc 407 int offset;
a50699fd
JA
408{
409 INTERVAL new = make_interval ();
410 int position = interval->position;
90ba40fc 411 int new_length = offset - 1;
a50699fd 412
a50699fd 413 new->position = interval->position;
90ba40fc 414 interval->position = interval->position + offset - 1;
a50699fd
JA
415 new->parent = interval;
416
417 if (NULL_LEFT_CHILD (interval))
418 {
419 interval->left = new;
420 new->total_length = new_length;
421
422 return new;
423 }
424
425 /* Insert the new node between INTERVAL and its left child. */
426 new->left = interval->left;
427 new->left->parent = new;
428 interval->left = new;
323a7ad4 429 new->total_length = new_length + LEFT_TOTAL_LENGTH (new);
a50699fd
JA
430
431 return new;
432}
433\f
90ba40fc
JA
434/* Find the interval containing text position POSITION in the text
435 represented by the interval tree TREE. POSITION is relative to
436 the beginning of that text.
a50699fd 437
90ba40fc
JA
438 The `position' field, which is a cache of an interval's position,
439 is updated in the interval found. Other functions (e.g., next_interval)
440 will update this cache based on the result of find_interval. */
441
442INLINE INTERVAL
a50699fd
JA
443find_interval (tree, position)
444 register INTERVAL tree;
445 register int position;
446{
447 register int relative_position = position;
448
449 if (NULL_INTERVAL_P (tree))
450 return NULL_INTERVAL;
451
452 if (position > TOTAL_LENGTH (tree))
453 abort (); /* Paranoia */
454#if 0
455 position = TOTAL_LENGTH (tree);
456#endif
457
458 while (1)
459 {
460 if (relative_position <= LEFT_TOTAL_LENGTH (tree))
461 {
462 tree = tree->left;
463 }
464 else if (relative_position > (TOTAL_LENGTH (tree)
465 - RIGHT_TOTAL_LENGTH (tree)))
466 {
467 relative_position -= (TOTAL_LENGTH (tree)
468 - RIGHT_TOTAL_LENGTH (tree));
469 tree = tree->right;
470 }
471 else
472 {
473 tree->position = LEFT_TOTAL_LENGTH (tree)
474 + position - relative_position + 1;
475 return tree;
476 }
477 }
478}
479\f
480/* Find the succeeding interval (lexicographically) to INTERVAL.
90ba40fc
JA
481 Sets the `position' field based on that of INTERVAL (see
482 find_interval). */
a50699fd
JA
483
484INTERVAL
485next_interval (interval)
486 register INTERVAL interval;
487{
488 register INTERVAL i = interval;
489 register int next_position;
490
491 if (NULL_INTERVAL_P (i))
492 return NULL_INTERVAL;
493 next_position = interval->position + LENGTH (interval);
494
495 if (! NULL_RIGHT_CHILD (i))
496 {
497 i = i->right;
498 while (! NULL_LEFT_CHILD (i))
499 i = i->left;
500
501 i->position = next_position;
502 return i;
503 }
504
505 while (! NULL_PARENT (i))
506 {
507 if (AM_LEFT_CHILD (i))
508 {
509 i = i->parent;
510 i->position = next_position;
511 return i;
512 }
513
514 i = i->parent;
515 }
516
517 return NULL_INTERVAL;
518}
519
520/* Find the preceding interval (lexicographically) to INTERVAL.
90ba40fc
JA
521 Sets the `position' field based on that of INTERVAL (see
522 find_interval). */
a50699fd
JA
523
524INTERVAL
525previous_interval (interval)
526 register INTERVAL interval;
527{
528 register INTERVAL i;
529 register position_of_previous;
530
531 if (NULL_INTERVAL_P (interval))
532 return NULL_INTERVAL;
533
534 if (! NULL_LEFT_CHILD (interval))
535 {
536 i = interval->left;
537 while (! NULL_RIGHT_CHILD (i))
538 i = i->right;
539
540 i->position = interval->position - LENGTH (i);
541 return i;
542 }
543
544 i = interval;
545 while (! NULL_PARENT (i))
546 {
547 if (AM_RIGHT_CHILD (i))
548 {
549 i = i->parent;
550
551 i->position = interval->position - LENGTH (i);
552 return i;
553 }
554 i = i->parent;
555 }
556
557 return NULL_INTERVAL;
558}
559\f
90ba40fc 560#if 0
a50699fd
JA
561/* Traverse a path down the interval tree TREE to the interval
562 containing POSITION, adjusting all nodes on the path for
563 an addition of LENGTH characters. Insertion between two intervals
564 (i.e., point == i->position, where i is second interval) means
565 text goes into second interval.
566
567 Modifications are needed to handle the hungry bits -- after simply
568 finding the interval at position (don't add length going down),
569 if it's the beginning of the interval, get the previous interval
570 and check the hugry bits of both. Then add the length going back up
571 to the root. */
572
573static INTERVAL
574adjust_intervals_for_insertion (tree, position, length)
575 INTERVAL tree;
576 int position, length;
577{
578 register int relative_position;
579 register INTERVAL this;
580
581 if (TOTAL_LENGTH (tree) == 0) /* Paranoia */
582 abort ();
583
584 /* If inserting at point-max of a buffer, that position
585 will be out of range */
586 if (position > TOTAL_LENGTH (tree))
587 position = TOTAL_LENGTH (tree);
588 relative_position = position;
589 this = tree;
590
591 while (1)
592 {
593 if (relative_position <= LEFT_TOTAL_LENGTH (this))
594 {
595 this->total_length += length;
596 this = this->left;
597 }
598 else if (relative_position > (TOTAL_LENGTH (this)
599 - RIGHT_TOTAL_LENGTH (this)))
600 {
601 relative_position -= (TOTAL_LENGTH (this)
602 - RIGHT_TOTAL_LENGTH (this));
603 this->total_length += length;
604 this = this->right;
605 }
606 else
607 {
608 /* If we are to use zero-length intervals as buffer pointers,
609 then this code will have to change. */
610 this->total_length += length;
611 this->position = LEFT_TOTAL_LENGTH (this)
612 + position - relative_position + 1;
613 return tree;
614 }
615 }
616}
90ba40fc
JA
617#endif
618
619/* Effect an adjustment corresponding to the addition of LENGTH characters
620 of text. Do this by finding the interval containing POSITION in the
621 interval tree TREE, and then adjusting all of it's ancestors by adding
622 LENGTH to them.
623
624 If POSITION is the first character of an interval, meaning that point
625 is actually between the two intervals, make the new text belong to
626 the interval which is "sticky".
627
1d1d7ba0 628 If both intervals are "sticky", then make them belong to the left-most
90ba40fc
JA
629 interval. Another possibility would be to create a new interval for
630 this text, and make it have the merged properties of both ends. */
631
632static INTERVAL
633adjust_intervals_for_insertion (tree, position, length)
634 INTERVAL tree;
635 int position, length;
636{
637 register INTERVAL i;
638
639 if (TOTAL_LENGTH (tree) == 0) /* Paranoia */
640 abort ();
641
642 /* If inserting at point-max of a buffer, that position
643 will be out of range. */
644 if (position > TOTAL_LENGTH (tree))
645 position = TOTAL_LENGTH (tree);
646
647 i = find_interval (tree, position);
648 /* If we are positioned between intervals, check the stickiness of
649 both of them. */
650 if (position == i->position
651 && position != 1)
652 {
249a6da9 653 register INTERVAL prev = previous_interval (i);
90ba40fc
JA
654
655 /* If both intervals are sticky here, then default to the
656 left-most one. But perhaps we should create a new
657 interval here instead... */
7b1d5b85 658 if (END_STICKY_P (prev))
90ba40fc
JA
659 i = prev;
660 }
661
662 while (! NULL_INTERVAL_P (i))
663 {
664 i->total_length += length;
249a6da9 665 i = i->parent;
90ba40fc
JA
666 }
667
668 return tree;
669}
a50699fd 670\f
90ba40fc
JA
671/* Delete an node I from its interval tree by merging its subtrees
672 into one subtree which is then returned. Caller is responsible for
a50699fd
JA
673 storing the resulting subtree into its parent. */
674
675static INTERVAL
676delete_node (i)
677 register INTERVAL i;
678{
679 register INTERVAL migrate, this;
680 register int migrate_amt;
681
682 if (NULL_INTERVAL_P (i->left))
683 return i->right;
684 if (NULL_INTERVAL_P (i->right))
685 return i->left;
686
687 migrate = i->left;
688 migrate_amt = i->left->total_length;
689 this = i->right;
690 this->total_length += migrate_amt;
691 while (! NULL_INTERVAL_P (this->left))
692 {
693 this = this->left;
694 this->total_length += migrate_amt;
695 }
696 this->left = migrate;
697 migrate->parent = this;
698
699 return i->right;
700}
701
702/* Delete interval I from its tree by calling `delete_node'
703 and properly connecting the resultant subtree.
704
705 I is presumed to be empty; that is, no adjustments are made
706 for the length of I. */
707
708void
709delete_interval (i)
710 register INTERVAL i;
711{
712 register INTERVAL parent;
713 int amt = LENGTH (i);
714
715 if (amt > 0) /* Only used on zero-length intervals now. */
716 abort ();
717
718 if (ROOT_INTERVAL_P (i))
719 {
720 Lisp_Object owner = (Lisp_Object) i->parent;
721 parent = delete_node (i);
722 if (! NULL_INTERVAL_P (parent))
723 parent->parent = (INTERVAL) owner;
724
725 if (XTYPE (owner) == Lisp_Buffer)
726 XBUFFER (owner)->intervals = parent;
727 else if (XTYPE (owner) == Lisp_String)
728 XSTRING (owner)->intervals = parent;
729 else
730 abort ();
731
732 return;
733 }
734
735 parent = i->parent;
736 if (AM_LEFT_CHILD (i))
737 {
738 parent->left = delete_node (i);
739 if (! NULL_INTERVAL_P (parent->left))
740 parent->left->parent = parent;
741 }
742 else
743 {
744 parent->right = delete_node (i);
745 if (! NULL_INTERVAL_P (parent->right))
746 parent->right->parent = parent;
747 }
748}
749\f
1d1d7ba0
JA
750/* Find the interval in TREE corresponding to the character position FROM
751 and delete as much as possible of AMOUNT from that interval, starting
752 after the relative position of FROM within it. Return the amount
753 actually deleted, and if the interval was zeroed-out, delete that
754 interval node from the tree.
a50699fd 755
1d1d7ba0
JA
756 Do this by recursing down TREE to the interval in question, and
757 deleting the appropriate amount of text. */
a50699fd
JA
758
759static int
760interval_deletion_adjustment (tree, from, amount)
761 register INTERVAL tree;
762 register int from, amount;
763{
764 register int relative_position = from;
765
766 if (NULL_INTERVAL_P (tree))
767 return 0;
768
769 /* Left branch */
770 if (relative_position <= LEFT_TOTAL_LENGTH (tree))
771 {
772 int subtract = interval_deletion_adjustment (tree->left,
773 relative_position,
774 amount);
775 tree->total_length -= subtract;
776 return subtract;
777 }
778 /* Right branch */
779 else if (relative_position > (TOTAL_LENGTH (tree)
780 - RIGHT_TOTAL_LENGTH (tree)))
781 {
782 int subtract;
783
784 relative_position -= (tree->total_length
785 - RIGHT_TOTAL_LENGTH (tree));
786 subtract = interval_deletion_adjustment (tree->right,
787 relative_position,
788 amount);
789 tree->total_length -= subtract;
790 return subtract;
791 }
792 /* Here -- this node */
793 else
794 {
795 /* If this is a zero-length, marker interval, then
796 we must skip it. */
797
798 if (relative_position == LEFT_TOTAL_LENGTH (tree) + 1)
799 {
800 /* This means we're deleting from the beginning of this interval. */
801 register int my_amount = LENGTH (tree);
802
803 if (amount < my_amount)
804 {
805 tree->total_length -= amount;
806 return amount;
807 }
808 else
809 {
810 tree->total_length -= my_amount;
811 if (LENGTH (tree) != 0)
812 abort (); /* Paranoia */
813
814 delete_interval (tree);
815 return my_amount;
816 }
817 }
818 else /* Deleting starting in the middle. */
819 {
820 register int my_amount = ((tree->total_length
821 - RIGHT_TOTAL_LENGTH (tree))
822 - relative_position + 1);
823
824 if (amount <= my_amount)
825 {
826 tree->total_length -= amount;
827 return amount;
828 }
829 else
830 {
831 tree->total_length -= my_amount;
832 return my_amount;
833 }
834 }
835 }
836
1d1d7ba0 837 /* Never reach here */
a50699fd
JA
838}
839
eb8c3be9 840/* Effect the adjustments necessary to the interval tree of BUFFER
1d1d7ba0
JA
841 to correspond to the deletion of LENGTH characters from that buffer
842 text. The deletion is effected at position START (relative to the
843 buffer). */
844
a50699fd
JA
845static void
846adjust_intervals_for_deletion (buffer, start, length)
847 struct buffer *buffer;
848 int start, length;
849{
850 register int left_to_delete = length;
851 register INTERVAL tree = buffer->intervals;
852 register int deleted;
853
854 if (NULL_INTERVAL_P (tree))
855 return;
856
857 if (length == TOTAL_LENGTH (tree))
858 {
859 buffer->intervals = NULL_INTERVAL;
860 return;
861 }
862
863 if (ONLY_INTERVAL_P (tree))
864 {
865 tree->total_length -= length;
866 return;
867 }
868
869 if (start > TOTAL_LENGTH (tree))
870 start = TOTAL_LENGTH (tree);
871 while (left_to_delete > 0)
872 {
873 left_to_delete -= interval_deletion_adjustment (tree, start,
874 left_to_delete);
875 tree = buffer->intervals;
876 if (left_to_delete == tree->total_length)
877 {
878 buffer->intervals = NULL_INTERVAL;
879 return;
880 }
881 }
882}
883\f
eb8c3be9 884/* Make the adjustments necessary to the interval tree of BUFFER to
1d1d7ba0
JA
885 represent an addition or deletion of LENGTH characters starting
886 at position START. Addition or deletion is indicated by the sign
887 of LENGTH. */
a50699fd
JA
888
889INLINE void
890offset_intervals (buffer, start, length)
891 struct buffer *buffer;
892 int start, length;
893{
894 if (NULL_INTERVAL_P (buffer->intervals) || length == 0)
895 return;
896
897 if (length > 0)
898 adjust_intervals_for_insertion (buffer->intervals, start, length);
899 else
900 adjust_intervals_for_deletion (buffer, start, -length);
901}
9c79dd1b
JA
902\f
903/* Merge interval I with its lexicographic successor. The resulting
904 interval is returned, and has the properties of the original
905 successor. The properties of I are lost. I is removed from the
906 interval tree.
907
908 IMPORTANT:
909 The caller must verify that this is not the last (rightmost)
910 interval. */
911
912INTERVAL
913merge_interval_right (i)
914 register INTERVAL i;
915{
916 register int absorb = LENGTH (i);
917 register INTERVAL successor;
918
919 /* Zero out this interval. */
920 i->total_length -= absorb;
921
922 /* Find the succeeding interval. */
923 if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb
924 as we descend. */
925 {
926 successor = i->right;
927 while (! NULL_LEFT_CHILD (successor))
928 {
929 successor->total_length += absorb;
930 successor = successor->left;
931 }
932
933 successor->total_length += absorb;
934 delete_interval (i);
935 return successor;
936 }
937
938 successor = i;
939 while (! NULL_PARENT (successor)) /* It's above us. Subtract as
940 we ascend. */
941 {
942 if (AM_LEFT_CHILD (successor))
943 {
944 successor = successor->parent;
945 delete_interval (i);
946 return successor;
947 }
948
949 successor = successor->parent;
950 successor->total_length -= absorb;
951 }
952
953 /* This must be the rightmost or last interval and cannot
954 be merged right. The caller should have known. */
955 abort ();
956}
957\f
958/* Merge interval I with its lexicographic predecessor. The resulting
959 interval is returned, and has the properties of the original predecessor.
960 The properties of I are lost. Interval node I is removed from the tree.
961
962 IMPORTANT:
963 The caller must verify that this is not the first (leftmost) interval. */
964
965INTERVAL
966merge_interval_left (i)
967 register INTERVAL i;
968{
969 register int absorb = LENGTH (i);
970 register INTERVAL predecessor;
971
972 /* Zero out this interval. */
973 i->total_length -= absorb;
974
975 /* Find the preceding interval. */
976 if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down,
977 adding ABSORB as we go. */
978 {
979 predecessor = i->left;
980 while (! NULL_RIGHT_CHILD (predecessor))
981 {
982 predecessor->total_length += absorb;
983 predecessor = predecessor->right;
984 }
985
986 predecessor->total_length += absorb;
987 delete_interval (i);
988 return predecessor;
989 }
990
991 predecessor = i;
992 while (! NULL_PARENT (predecessor)) /* It's above us. Go up,
993 subtracting ABSORB. */
994 {
995 if (AM_RIGHT_CHILD (predecessor))
996 {
997 predecessor = predecessor->parent;
998 delete_interval (i);
999 return predecessor;
1000 }
1001
1002 predecessor = predecessor->parent;
1003 predecessor->total_length -= absorb;
1004 }
a50699fd 1005
9c79dd1b
JA
1006 /* This must be the leftmost or first interval and cannot
1007 be merged left. The caller should have known. */
1008 abort ();
1009}
1010\f
1d1d7ba0
JA
1011/* Make an exact copy of interval tree SOURCE which descends from
1012 PARENT. This is done by recursing through SOURCE, copying
1013 the current interval and its properties, and then adjusting
1014 the pointers of the copy. */
1015
a50699fd
JA
1016static INTERVAL
1017reproduce_tree (source, parent)
1018 INTERVAL source, parent;
1019{
1020 register INTERVAL t = make_interval ();
1021
1022 bcopy (source, t, INTERVAL_SIZE);
1023 copy_properties (source, t);
1024 t->parent = parent;
1025 if (! NULL_LEFT_CHILD (source))
1026 t->left = reproduce_tree (source->left, t);
1027 if (! NULL_RIGHT_CHILD (source))
1028 t->right = reproduce_tree (source->right, t);
1029
1030 return t;
1031}
1032
1d1d7ba0
JA
1033/* Make a new interval of length LENGTH starting at START in the
1034 group of intervals INTERVALS, which is actually an interval tree.
1035 Returns the new interval.
1036
1037 Generate an error if the new positions would overlap an existing
1038 interval. */
1039
a50699fd
JA
1040static INTERVAL
1041make_new_interval (intervals, start, length)
1042 INTERVAL intervals;
1043 int start, length;
1044{
1045 INTERVAL slot;
1046
1047 slot = find_interval (intervals, start);
1048 if (start + length > slot->position + LENGTH (slot))
1049 error ("Interval would overlap");
1050
1051 if (start == slot->position && length == LENGTH (slot))
1052 return slot;
1053
1054 if (slot->position == start)
1055 {
1056 /* New right node. */
1057 split_interval_right (slot, length + 1);
1058 return slot;
1059 }
1060
1061 if (slot->position + LENGTH (slot) == start + length)
1062 {
1063 /* New left node. */
1064 split_interval_left (slot, LENGTH (slot) - length + 1);
1065 return slot;
1066 }
1067
1068 /* Convert interval SLOT into three intervals. */
1069 split_interval_left (slot, start - slot->position + 1);
1070 split_interval_right (slot, length + 1);
1071 return slot;
1072}
294efdbe 1073\f
9c79dd1b 1074/* Insert the intervals of SOURCE into BUFFER at POSITION.
a50699fd
JA
1075
1076 This is used in insdel.c when inserting Lisp_Strings into
9c79dd1b 1077 the buffer. The text corresponding to SOURCE is already in
a50699fd
JA
1078 the buffer when this is called. The intervals of new tree are
1079 those belonging to the string being inserted; a copy is not made.
1080
1081 If the inserted text had no intervals associated, this function
1082 simply returns -- offset_intervals should handle placing the
90ba40fc 1083 text in the correct interval, depending on the sticky bits.
a50699fd
JA
1084
1085 If the inserted text had properties (intervals), then there are two
1086 cases -- either insertion happened in the middle of some interval,
1087 or between two intervals.
1088
1089 If the text goes into the middle of an interval, then new
1090 intervals are created in the middle with only the properties of
1091 the new text, *unless* the macro MERGE_INSERTIONS is true, in
1092 which case the new text has the union of its properties and those
1093 of the text into which it was inserted.
1094
1095 If the text goes between two intervals, then if neither interval
90ba40fc
JA
1096 had its appropriate sticky property set (front_sticky, rear_sticky),
1097 the new text has only its properties. If one of the sticky properties
a50699fd 1098 is set, then the new text "sticks" to that region and its properties
eb8c3be9 1099 depend on merging as above. If both the preceding and succeeding
90ba40fc
JA
1100 intervals to the new text are "sticky", then the new text retains
1101 only its properties, as if neither sticky property were set. Perhaps
a50699fd
JA
1102 we should consider merging all three sets of properties onto the new
1103 text... */
1104
1105void
9c79dd1b
JA
1106graft_intervals_into_buffer (source, position, buffer)
1107 INTERVAL source;
a50699fd 1108 int position;
9c79dd1b 1109 struct buffer *buffer;
a50699fd 1110{
323a7ad4 1111 register INTERVAL under, over, this, prev;
9c79dd1b 1112 register INTERVAL tree = buffer->intervals;
323a7ad4 1113 int middle;
a50699fd
JA
1114
1115 /* If the new text has no properties, it becomes part of whatever
323a7ad4 1116 interval it was inserted into. */
9c79dd1b 1117 if (NULL_INTERVAL_P (source))
a50699fd
JA
1118 return;
1119
a50699fd
JA
1120 if (NULL_INTERVAL_P (tree))
1121 {
1122 /* The inserted text constitutes the whole buffer, so
1123 simply copy over the interval structure. */
249a6da9 1124 if (BUF_Z (buffer) == TOTAL_LENGTH (source))
a50699fd 1125 {
9c79dd1b 1126 buffer->intervals = reproduce_tree (source, tree->parent);
a50699fd
JA
1127 /* Explicitly free the old tree here. */
1128
1129 return;
1130 }
1131
1132 /* Create an interval tree in which to place a copy
323a7ad4 1133 of the intervals of the inserted string. */
a50699fd 1134 {
249a6da9
JA
1135 Lisp_Object buf;
1136 XSET (buf, Lisp_Buffer, buffer);
323a7ad4 1137 tree = create_root_interval (buf);
a50699fd
JA
1138 }
1139 }
1140 else
9c79dd1b 1141 if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (source))
323a7ad4
RS
1142 /* If the buffer contains only the new string, but
1143 there was already some interval tree there, then it may be
1144 some zero length intervals. Eventually, do something clever
1145 about inserting properly. For now, just waste the old intervals. */
1146 {
1147 buffer->intervals = reproduce_tree (source, tree->parent);
1148 /* Explicitly free the old tree here. */
a50699fd 1149
323a7ad4
RS
1150 return;
1151 }
1152 else
1153 /* Paranoia -- the text has already been added, so this buffer
1154 should be of non-zero length. */
1155 if (TOTAL_LENGTH (tree) == 0)
1156 abort ();
a50699fd
JA
1157
1158 this = under = find_interval (tree, position);
1159 if (NULL_INTERVAL_P (under)) /* Paranoia */
1160 abort ();
9c79dd1b 1161 over = find_interval (source, 1);
a50699fd 1162
323a7ad4
RS
1163 /* Here for insertion in the middle of an interval.
1164 Split off an equivalent interval to the right,
1165 then don't bother with it any more. */
a50699fd 1166
323a7ad4 1167 if (position > under->position)
a50699fd
JA
1168 {
1169 INTERVAL end_unchanged
323a7ad4 1170 = split_interval_left (this, position - under->position + 1);
a50699fd 1171 copy_properties (under, end_unchanged);
323a7ad4
RS
1172 under->position = position;
1173 prev = 0;
1174 middle = 1;
a50699fd 1175 }
323a7ad4
RS
1176 else
1177 {
1178 prev = previous_interval (under);
1179 if (prev && !END_STICKY_P (prev))
1180 prev = 0;
1181 }
1182
1183 /* Insertion is now at beginning of UNDER. */
a50699fd 1184
323a7ad4
RS
1185 /* The inserted text "sticks" to the interval `under',
1186 which means it gets those properties. */
a50699fd
JA
1187 while (! NULL_INTERVAL_P (over))
1188 {
323a7ad4
RS
1189 position = LENGTH (over) + 1;
1190 if (position < LENGTH (under))
1191 this = split_interval_left (under, position);
1192 else
1193 this = under;
a50699fd 1194 copy_properties (over, this);
323a7ad4
RS
1195 /* Insertion at the end of an interval, PREV,
1196 inherits from PREV if PREV is sticky at the end. */
1197 if (prev && ! FRONT_STICKY_P (under)
1198 && MERGE_INSERTIONS (prev))
1199 merge_properties (prev, this);
1200 /* Maybe it inherits from the following interval
1201 if that is sticky at the front. */
1202 else if ((FRONT_STICKY_P (under) || middle)
1203 && MERGE_INSERTIONS (under))
a50699fd 1204 merge_properties (under, this);
a50699fd
JA
1205 over = next_interval (over);
1206 }
1207
9c79dd1b 1208 buffer->intervals = balance_intervals (buffer->intervals);
a50699fd
JA
1209 return;
1210}
1211
5cae0ec6
RS
1212/* Get the value of property PROP from PLIST,
1213 which is the plist of an interval.
1214 We check for direct properties and for categories with property PROP. */
1215
1216Lisp_Object
323a7ad4
RS
1217textget (plist, prop)
1218 Lisp_Object plist;
1219 register Lisp_Object prop;
1220{
5cae0ec6
RS
1221 register Lisp_Object tail, fallback;
1222 fallback = Qnil;
323a7ad4
RS
1223
1224 for (tail = plist; !NILP (tail); tail = Fcdr (Fcdr (tail)))
1225 {
1226 register Lisp_Object tem;
1227 tem = Fcar (tail);
1228 if (EQ (prop, tem))
1229 return Fcar (Fcdr (tail));
5cae0ec6
RS
1230 if (EQ (tem, Qcategory))
1231 fallback = Fget (Fcar (Fcdr (tail)), prop);
323a7ad4 1232 }
5cae0ec6
RS
1233
1234 return fallback;
323a7ad4 1235}
294efdbe 1236\f
5cae0ec6
RS
1237/* Set point in BUFFER to POSITION. If the target position is
1238 before an invisible character which is not displayed with a special glyph,
323a7ad4 1239 move back to an ok place to display. */
a50699fd
JA
1240
1241void
1242set_point (position, buffer)
1243 register int position;
1244 register struct buffer *buffer;
1245{
323a7ad4 1246 register INTERVAL to, from, toprev, fromprev, target;
a50699fd
JA
1247 register int iposition = position;
1248 int buffer_point;
1249 register Lisp_Object obj;
1250 int backwards = (position < BUF_PT (buffer)) ? 1 : 0;
9c79dd1b 1251 int old_position = buffer->text.pt;
a50699fd
JA
1252
1253 if (position == buffer->text.pt)
1254 return;
1255
62056764
JB
1256 /* Check this now, before checking if the buffer has any intervals.
1257 That way, we can catch conditions which break this sanity check
1258 whether or not there are intervals in the buffer. */
1259 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer))
1260 abort ();
1261
a50699fd
JA
1262 if (NULL_INTERVAL_P (buffer->intervals))
1263 {
1264 buffer->text.pt = position;
1265 return;
1266 }
1267
a50699fd 1268 /* Position Z is really one past the last char in the buffer. */
294efdbe 1269 if (position == BUF_ZV (buffer))
a50699fd
JA
1270 iposition = position - 1;
1271
323a7ad4
RS
1272 /* Set TO to the interval containing the char after POSITION,
1273 and TOPREV to the interval containing the char before POSITION.
1274 Either one may be null. They may be equal. */
a50699fd 1275 to = find_interval (buffer->intervals, iposition);
294efdbe
RS
1276 if (position == BUF_BEGV (buffer))
1277 toprev = 0;
1278 else if (to->position == position)
323a7ad4
RS
1279 toprev = previous_interval (to);
1280 else if (iposition != position)
1281 toprev = to, to = 0;
1282 else
1283 toprev = to;
1284
294efdbe
RS
1285 buffer_point = (BUF_PT (buffer) == BUF_ZV (buffer)
1286 ? BUF_ZV (buffer) - 1
323a7ad4 1287 : BUF_PT (buffer));
9c79dd1b 1288
323a7ad4
RS
1289 /* Set FROM to the interval containing the char after PT,
1290 and FROMPREV to the interval containing the char before PT.
1291 Either one may be null. They may be equal. */
9c79dd1b 1292 /* We could cache this and save time. */
a50699fd 1293 from = find_interval (buffer->intervals, buffer_point);
294efdbe
RS
1294 if (from->position == BUF_BEGV (buffer))
1295 fromprev = 0;
1296 else if (from->position == BUF_PT (buffer))
323a7ad4
RS
1297 fromprev = previous_interval (from);
1298 else if (buffer_point != BUF_PT (buffer))
1299 fromprev = from, from = 0;
1300 else
1301 fromprev = from;
a50699fd
JA
1302
1303 /* Moving within an interval */
323a7ad4 1304 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to))
a50699fd
JA
1305 {
1306 buffer->text.pt = position;
1307 return;
1308 }
1309
5cae0ec6
RS
1310 /* If the new position is before an invisible character,
1311 move forward over all such. */
1312 while (! NULL_INTERVAL_P (to)
1313 && ! INTERVAL_VISIBLE_P (to)
1314 && ! DISPLAY_INVISIBLE_GLYPH (to))
a50699fd 1315 {
5cae0ec6
RS
1316 toprev = to;
1317 to = next_interval (to);
323a7ad4 1318 position = to->position;
a50699fd 1319 }
323a7ad4
RS
1320
1321 buffer->text.pt = position;
a50699fd 1322
d7e3e52b
JA
1323 /* We run point-left and point-entered hooks here, iff the
1324 two intervals are not equivalent. These hooks take
323a7ad4
RS
1325 (old_point, new_point) as arguments. */
1326 if (! intervals_equal (from, to)
1327 || ! intervals_equal (fromprev, toprev))
9c79dd1b 1328 {
323a7ad4
RS
1329 Lisp_Object leave_after, leave_before, enter_after, enter_before;
1330
1331 if (fromprev)
1332 leave_after = textget (fromprev->plist, Qpoint_left);
1333 else
1334 leave_after = Qnil;
1335 if (from)
1336 leave_before = textget (from->plist, Qpoint_left);
1337 else
1338 leave_before = Qnil;
1339
1340 if (toprev)
1341 enter_after = textget (toprev->plist, Qpoint_entered);
1342 else
1343 enter_after = Qnil;
1344 if (to)
1345 enter_before = textget (to->plist, Qpoint_entered);
1346 else
1347 enter_before = Qnil;
9c79dd1b 1348
323a7ad4
RS
1349 if (! EQ (leave_before, enter_before) && !NILP (leave_before))
1350 call2 (leave_before, old_position, position);
1351 if (! EQ (leave_after, enter_after) && !NILP (leave_after))
1352 call2 (leave_after, old_position, position);
9c79dd1b 1353
323a7ad4
RS
1354 if (! EQ (enter_before, leave_before) && !NILP (enter_before))
1355 call2 (enter_before, old_position, position);
1356 if (! EQ (enter_after, leave_after) && !NILP (enter_after))
1357 call2 (enter_after, old_position, position);
9c79dd1b 1358 }
a50699fd
JA
1359}
1360
9c79dd1b 1361/* Set point temporarily, without checking any text properties. */
a50699fd 1362
9c79dd1b
JA
1363INLINE void
1364temp_set_point (position, buffer)
1365 int position;
1366 struct buffer *buffer;
1367{
1368 buffer->text.pt = position;
1369}
294efdbe 1370\f
5cae0ec6
RS
1371/* Return the proper local map for position POSITION in BUFFER.
1372 Use the map specified by the local-map property, if any.
1373 Otherwise, use BUFFER's local map. */
1374
1375Lisp_Object
1376get_local_map (position, buffer)
1377 register int position;
1378 register struct buffer *buffer;
1379{
1380 register INTERVAL interval;
1381 Lisp_Object prop, tem;
1382
1383 if (NULL_INTERVAL_P (buffer->intervals))
1384 return current_buffer->keymap;
1385
1386 /* Perhaps we should just change `position' to the limit. */
1387 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer))
1388 abort ();
1389
1390 /* Position Z is really one past the last char in the buffer. */
1391 if (position == BUF_ZV (buffer))
1392 return current_buffer->keymap;
1393
1394 interval = find_interval (buffer->intervals, position);
1395 prop = textget (interval->plist, Qlocal_map);
1396 if (NILP (prop))
1397 return current_buffer->keymap;
1398
1399 /* Use the local map only if it is valid. */
1400 tem = Fkeymapp (prop);
1401 if (!NILP (tem))
1402 return prop;
1403
1404 return current_buffer->keymap;
1405}
1406\f
294efdbe
RS
1407/* Call the modification hook functions in LIST, each with START and END. */
1408
1409static void
1410call_mod_hooks (list, start, end)
1411 Lisp_Object list, start, end;
1412{
1413 struct gcpro gcpro1;
1414 GCPRO1 (list);
1415 while (!NILP (list))
1416 {
1417 call2 (Fcar (list), start, end);
1418 list = Fcdr (list);
1419 }
1420 UNGCPRO;
1421}
9c79dd1b
JA
1422
1423/* Check for read-only intervals and signal an error if we find one.
1424 Then check for any modification hooks in the range START up to
1425 (but not including) TO. Create a list of all these hooks in
1426 lexicographic order, eliminating consecutive extra copies of the
1427 same hook. Then call those hooks in order, with START and END - 1
1428 as arguments. */
a50699fd
JA
1429
1430void
1431verify_interval_modification (buf, start, end)
1432 struct buffer *buf;
1433 int start, end;
1434{
1435 register INTERVAL intervals = buf->intervals;
294efdbe
RS
1436 register INTERVAL i, prev;
1437 Lisp_Object hooks;
1438 register Lisp_Object prev_mod_hooks;
1439 Lisp_Object mod_hooks;
9c79dd1b 1440 struct gcpro gcpro1;
a50699fd 1441
294efdbe
RS
1442 hooks = Qnil;
1443 prev_mod_hooks = Qnil;
1444 mod_hooks = Qnil;
1445
a50699fd
JA
1446 if (NULL_INTERVAL_P (intervals))
1447 return;
1448
1449 if (start > end)
1450 {
1451 int temp = start;
1452 start = end;
1453 end = temp;
1454 }
1455
294efdbe
RS
1456 /* For an insert operation, check the two chars around the position. */
1457 if (start == end)
a50699fd 1458 {
294efdbe
RS
1459 INTERVAL prev;
1460 Lisp_Object before, after;
a50699fd 1461
294efdbe
RS
1462 /* Set I to the interval containing the char after START,
1463 and PREV to the interval containing the char before START.
1464 Either one may be null. They may be equal. */
1465 i = find_interval (intervals,
1466 (start == BUF_ZV (buf) ? start - 1 : start));
1467
1468 if (start == BUF_BEGV (buf))
1469 prev = 0;
1470 if (i->position == start)
1471 prev = previous_interval (i);
1472 else if (i->position < start)
1473 prev = i;
1474 if (start == BUF_ZV (buf))
1475 i = 0;
1476
1477 if (NULL_INTERVAL_P (prev))
1478 {
5cae0ec6 1479 after = textget (i->plist, Qread_only);
294efdbe
RS
1480 if (! NILP (after))
1481 error ("Attempt to insert within read-only text");
1482 }
1483 else if (NULL_INTERVAL_P (i))
1484 {
5cae0ec6 1485 before = textget (prev->plist, Qread_only);
294efdbe
RS
1486 if (! NILP (before))
1487 error ("Attempt to insert within read-only text");
1488 }
1489 else
1490 {
5cae0ec6
RS
1491 before = textget (prev->plist, Qread_only);
1492 after = textget (i->plist, Qread_only);
294efdbe
RS
1493 if (! NILP (before) && EQ (before, after))
1494 error ("Attempt to insert within read-only text");
1495 }
1496
1497 /* Run both mod hooks (just once if they're the same). */
1498 if (!NULL_INTERVAL_P (prev))
1499 prev_mod_hooks = textget (prev->plist, Qmodification_hooks);
1500 if (!NULL_INTERVAL_P (i))
1501 mod_hooks = textget (i->plist, Qmodification_hooks);
1502 GCPRO1 (mod_hooks);
1503 if (! NILP (prev_mod_hooks))
1504 call_mod_hooks (prev_mod_hooks, make_number (start),
1505 make_number (end));
1506 UNGCPRO;
1507 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1508 call_mod_hooks (mod_hooks, make_number (start), make_number (end));
a50699fd
JA
1509 }
1510 else
a50699fd 1511 {
294efdbe
RS
1512 /* Loop over intervals on or next to START...END,
1513 collecting their hooks. */
9c79dd1b 1514
294efdbe
RS
1515 i = find_interval (intervals, start);
1516 do
9c79dd1b 1517 {
294efdbe
RS
1518 if (! INTERVAL_WRITABLE_P (i))
1519 error ("Attempt to modify read-only text");
9c79dd1b 1520
294efdbe
RS
1521 mod_hooks = textget (i->plist, Qmodification_hooks);
1522 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1523 {
1524 hooks = Fcons (mod_hooks, hooks);
1525 prev_mod_hooks = mod_hooks;
1526 }
a50699fd 1527
294efdbe
RS
1528 i = next_interval (i);
1529 }
1530 /* Keep going thru the interval containing the char before END. */
1531 while (! NULL_INTERVAL_P (i) && i->position < end);
1532
1533 GCPRO1 (hooks);
1534 hooks = Fnreverse (hooks);
1535 while (! EQ (hooks, Qnil))
1536 {
1537 call_mod_hooks (Fcar (hooks), make_number (start),
1538 make_number (end));
1539 hooks = Fcdr (hooks);
1540 }
1541 UNGCPRO;
9c79dd1b 1542 }
a50699fd
JA
1543}
1544
1545/* Balance an interval node if the amount of text in its left and right
1546 subtrees differs by more than the percentage specified by
1547 `interval-balance-threshold'. */
1548
1549static INTERVAL
1550balance_an_interval (i)
1551 INTERVAL i;
1552{
1553 register int total_children_size = (LEFT_TOTAL_LENGTH (i)
1554 + RIGHT_TOTAL_LENGTH (i));
1555 register int threshold = (XFASTINT (interval_balance_threshold)
1556 * (total_children_size / 100));
1557
95e3e1ef
RS
1558 /* Balance within each side. */
1559 balance_intervals (i->left);
1560 balance_intervals (i->right);
a50699fd
JA
1561
1562 if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
1563 && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
95e3e1ef
RS
1564 {
1565 i = rotate_right (i);
1566 /* If that made it unbalanced the other way, take it back. */
1567 if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
1568 && (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
1569 return rotate_left (i);
1570 return i;
1571 }
a50699fd 1572
95e3e1ef
RS
1573 if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
1574 && (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
1575 {
1576 i = rotate_left (i);
1577 if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
1578 && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
1579 return rotate_right (i);
1580 return i;
1581 }
a50699fd
JA
1582
1583 return i;
1584}
1585
1586/* Balance the interval tree TREE. Balancing is by weight
1587 (the amount of text). */
1588
1589INTERVAL
1590balance_intervals (tree)
1591 register INTERVAL tree;
1592{
1593 register INTERVAL new_tree;
1594
1595 if (NULL_INTERVAL_P (tree))
1596 return NULL_INTERVAL;
1597
1598 new_tree = tree;
1599 do
1600 {
1601 tree = new_tree;
1602 new_tree = balance_an_interval (new_tree);
1603 }
1604 while (new_tree != tree);
1605
1606 return new_tree;
1607}
1608
9c79dd1b 1609/* Produce an interval tree reflecting the intervals in
a50699fd
JA
1610 TREE from START to START + LENGTH. */
1611
7b1d5b85 1612INTERVAL
a50699fd
JA
1613copy_intervals (tree, start, length)
1614 INTERVAL tree;
1615 int start, length;
1616{
1617 register INTERVAL i, new, t;
95e3e1ef 1618 register int got, prevlen;
a50699fd
JA
1619
1620 if (NULL_INTERVAL_P (tree) || length <= 0)
1621 return NULL_INTERVAL;
1622
1623 i = find_interval (tree, start);
1624 if (NULL_INTERVAL_P (i) || LENGTH (i) == 0)
1625 abort ();
1626
1627 /* If there is only one interval and it's the default, return nil. */
1628 if ((start - i->position + 1 + length) < LENGTH (i)
1629 && DEFAULT_INTERVAL_P (i))
1630 return NULL_INTERVAL;
1631
1632 new = make_interval ();
1633 new->position = 1;
1634 got = (LENGTH (i) - (start - i->position));
9c79dd1b 1635 new->total_length = length;
a50699fd
JA
1636 copy_properties (i, new);
1637
1638 t = new;
95e3e1ef 1639 prevlen = got;
a50699fd
JA
1640 while (got < length)
1641 {
1642 i = next_interval (i);
95e3e1ef 1643 t = split_interval_right (t, prevlen + 1);
a50699fd 1644 copy_properties (i, t);
95e3e1ef
RS
1645 prevlen = LENGTH (i);
1646 got += prevlen;
a50699fd
JA
1647 }
1648
a50699fd
JA
1649 return balance_intervals (new);
1650}
1651
a50699fd
JA
1652/* Give STRING the properties of BUFFER from POSITION to LENGTH. */
1653
d7e3e52b 1654INLINE void
a50699fd
JA
1655copy_intervals_to_string (string, buffer, position, length)
1656 Lisp_Object string, buffer;
1657 int position, length;
1658{
1659 INTERVAL interval_copy = copy_intervals (XBUFFER (buffer)->intervals,
1660 position, length);
1661 if (NULL_INTERVAL_P (interval_copy))
1662 return;
1663
1664 interval_copy->parent = (INTERVAL) string;
1665 XSTRING (string)->intervals = interval_copy;
1666}
d2f7a802
JA
1667
1668#endif /* USE_TEXT_PROPERTIES */