* dispnew.c (direct_output_for_insert): By the time this function
[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);
734c51b2 162 if (! EQ (i1_val, Fcar (i0_cdr)))
a50699fd
JA
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 434/* Find the interval containing text position POSITION in the text
24e3d3bf
JB
435 represented by the interval tree TREE. POSITION is a buffer
436 position; the earliest position is 1. If POSITION is at the end of
437 the buffer, return the interval containing the last character.
a50699fd 438
90ba40fc
JA
439 The `position' field, which is a cache of an interval's position,
440 is updated in the interval found. Other functions (e.g., next_interval)
441 will update this cache based on the result of find_interval. */
442
443INLINE INTERVAL
a50699fd
JA
444find_interval (tree, position)
445 register INTERVAL tree;
446 register int position;
447{
24e3d3bf
JB
448 /* The distance from the left edge of the subtree at TREE
449 to POSITION. */
450 register int relative_position = position - BEG;
a50699fd
JA
451
452 if (NULL_INTERVAL_P (tree))
453 return NULL_INTERVAL;
454
24e3d3bf 455 if (relative_position > TOTAL_LENGTH (tree))
a50699fd 456 abort (); /* Paranoia */
a50699fd
JA
457
458 while (1)
459 {
24e3d3bf 460 if (relative_position < LEFT_TOTAL_LENGTH (tree))
a50699fd
JA
461 {
462 tree = tree->left;
463 }
24e3d3bf
JB
464 else if (! NULL_RIGHT_CHILD (tree)
465 && relative_position >= (TOTAL_LENGTH (tree)
466 - RIGHT_TOTAL_LENGTH (tree)))
a50699fd
JA
467 {
468 relative_position -= (TOTAL_LENGTH (tree)
469 - RIGHT_TOTAL_LENGTH (tree));
470 tree = tree->right;
471 }
472 else
473 {
24e3d3bf
JB
474 tree->position =
475 (position - relative_position /* the left edge of *tree */
476 + LEFT_TOTAL_LENGTH (tree)); /* the left edge of this interval */
477
a50699fd
JA
478 return tree;
479 }
480 }
481}
482\f
483/* Find the succeeding interval (lexicographically) to INTERVAL.
90ba40fc
JA
484 Sets the `position' field based on that of INTERVAL (see
485 find_interval). */
a50699fd
JA
486
487INTERVAL
488next_interval (interval)
489 register INTERVAL interval;
490{
491 register INTERVAL i = interval;
492 register int next_position;
493
494 if (NULL_INTERVAL_P (i))
495 return NULL_INTERVAL;
496 next_position = interval->position + LENGTH (interval);
497
498 if (! NULL_RIGHT_CHILD (i))
499 {
500 i = i->right;
501 while (! NULL_LEFT_CHILD (i))
502 i = i->left;
503
504 i->position = next_position;
505 return i;
506 }
507
508 while (! NULL_PARENT (i))
509 {
510 if (AM_LEFT_CHILD (i))
511 {
512 i = i->parent;
513 i->position = next_position;
514 return i;
515 }
516
517 i = i->parent;
518 }
519
520 return NULL_INTERVAL;
521}
522
523/* Find the preceding interval (lexicographically) to INTERVAL.
90ba40fc
JA
524 Sets the `position' field based on that of INTERVAL (see
525 find_interval). */
a50699fd
JA
526
527INTERVAL
528previous_interval (interval)
529 register INTERVAL interval;
530{
531 register INTERVAL i;
532 register position_of_previous;
533
534 if (NULL_INTERVAL_P (interval))
535 return NULL_INTERVAL;
536
537 if (! NULL_LEFT_CHILD (interval))
538 {
539 i = interval->left;
540 while (! NULL_RIGHT_CHILD (i))
541 i = i->right;
542
543 i->position = interval->position - LENGTH (i);
544 return i;
545 }
546
547 i = interval;
548 while (! NULL_PARENT (i))
549 {
550 if (AM_RIGHT_CHILD (i))
551 {
552 i = i->parent;
553
554 i->position = interval->position - LENGTH (i);
555 return i;
556 }
557 i = i->parent;
558 }
559
560 return NULL_INTERVAL;
561}
562\f
90ba40fc 563#if 0
a50699fd
JA
564/* Traverse a path down the interval tree TREE to the interval
565 containing POSITION, adjusting all nodes on the path for
566 an addition of LENGTH characters. Insertion between two intervals
567 (i.e., point == i->position, where i is second interval) means
568 text goes into second interval.
569
570 Modifications are needed to handle the hungry bits -- after simply
571 finding the interval at position (don't add length going down),
572 if it's the beginning of the interval, get the previous interval
573 and check the hugry bits of both. Then add the length going back up
574 to the root. */
575
576static INTERVAL
577adjust_intervals_for_insertion (tree, position, length)
578 INTERVAL tree;
579 int position, length;
580{
581 register int relative_position;
582 register INTERVAL this;
583
584 if (TOTAL_LENGTH (tree) == 0) /* Paranoia */
585 abort ();
586
587 /* If inserting at point-max of a buffer, that position
588 will be out of range */
589 if (position > TOTAL_LENGTH (tree))
590 position = TOTAL_LENGTH (tree);
591 relative_position = position;
592 this = tree;
593
594 while (1)
595 {
596 if (relative_position <= LEFT_TOTAL_LENGTH (this))
597 {
598 this->total_length += length;
599 this = this->left;
600 }
601 else if (relative_position > (TOTAL_LENGTH (this)
602 - RIGHT_TOTAL_LENGTH (this)))
603 {
604 relative_position -= (TOTAL_LENGTH (this)
605 - RIGHT_TOTAL_LENGTH (this));
606 this->total_length += length;
607 this = this->right;
608 }
609 else
610 {
611 /* If we are to use zero-length intervals as buffer pointers,
612 then this code will have to change. */
613 this->total_length += length;
614 this->position = LEFT_TOTAL_LENGTH (this)
615 + position - relative_position + 1;
616 return tree;
617 }
618 }
619}
90ba40fc
JA
620#endif
621
622/* Effect an adjustment corresponding to the addition of LENGTH characters
623 of text. Do this by finding the interval containing POSITION in the
624 interval tree TREE, and then adjusting all of it's ancestors by adding
625 LENGTH to them.
626
627 If POSITION is the first character of an interval, meaning that point
628 is actually between the two intervals, make the new text belong to
629 the interval which is "sticky".
630
1d1d7ba0 631 If both intervals are "sticky", then make them belong to the left-most
90ba40fc
JA
632 interval. Another possibility would be to create a new interval for
633 this text, and make it have the merged properties of both ends. */
634
635static INTERVAL
636adjust_intervals_for_insertion (tree, position, length)
637 INTERVAL tree;
638 int position, length;
639{
640 register INTERVAL i;
641
642 if (TOTAL_LENGTH (tree) == 0) /* Paranoia */
643 abort ();
644
24e3d3bf
JB
645 /* If inserting at point-max of a buffer, that position will be out
646 of range. Remember that buffer positions are 1-based. */
647 if (position > BEG + TOTAL_LENGTH (tree))
648 position = BEG + TOTAL_LENGTH (tree);
90ba40fc
JA
649
650 i = find_interval (tree, position);
651 /* If we are positioned between intervals, check the stickiness of
652 both of them. */
653 if (position == i->position
24e3d3bf 654 && position != BEG)
90ba40fc 655 {
249a6da9 656 register INTERVAL prev = previous_interval (i);
90ba40fc
JA
657
658 /* If both intervals are sticky here, then default to the
659 left-most one. But perhaps we should create a new
660 interval here instead... */
eebaeadd 661 if (END_STICKY_P (prev) || ! FRONT_STICKY_P (i))
90ba40fc
JA
662 i = prev;
663 }
664
665 while (! NULL_INTERVAL_P (i))
666 {
667 i->total_length += length;
249a6da9 668 i = i->parent;
90ba40fc
JA
669 }
670
671 return tree;
672}
a50699fd 673\f
90ba40fc
JA
674/* Delete an node I from its interval tree by merging its subtrees
675 into one subtree which is then returned. Caller is responsible for
a50699fd
JA
676 storing the resulting subtree into its parent. */
677
678static INTERVAL
679delete_node (i)
680 register INTERVAL i;
681{
682 register INTERVAL migrate, this;
683 register int migrate_amt;
684
685 if (NULL_INTERVAL_P (i->left))
686 return i->right;
687 if (NULL_INTERVAL_P (i->right))
688 return i->left;
689
690 migrate = i->left;
691 migrate_amt = i->left->total_length;
692 this = i->right;
693 this->total_length += migrate_amt;
694 while (! NULL_INTERVAL_P (this->left))
695 {
696 this = this->left;
697 this->total_length += migrate_amt;
698 }
699 this->left = migrate;
700 migrate->parent = this;
701
702 return i->right;
703}
704
705/* Delete interval I from its tree by calling `delete_node'
706 and properly connecting the resultant subtree.
707
708 I is presumed to be empty; that is, no adjustments are made
709 for the length of I. */
710
711void
712delete_interval (i)
713 register INTERVAL i;
714{
715 register INTERVAL parent;
716 int amt = LENGTH (i);
717
718 if (amt > 0) /* Only used on zero-length intervals now. */
719 abort ();
720
721 if (ROOT_INTERVAL_P (i))
722 {
723 Lisp_Object owner = (Lisp_Object) i->parent;
724 parent = delete_node (i);
725 if (! NULL_INTERVAL_P (parent))
726 parent->parent = (INTERVAL) owner;
727
728 if (XTYPE (owner) == Lisp_Buffer)
729 XBUFFER (owner)->intervals = parent;
730 else if (XTYPE (owner) == Lisp_String)
731 XSTRING (owner)->intervals = parent;
732 else
733 abort ();
734
735 return;
736 }
737
738 parent = i->parent;
739 if (AM_LEFT_CHILD (i))
740 {
741 parent->left = delete_node (i);
742 if (! NULL_INTERVAL_P (parent->left))
743 parent->left->parent = parent;
744 }
745 else
746 {
747 parent->right = delete_node (i);
748 if (! NULL_INTERVAL_P (parent->right))
749 parent->right->parent = parent;
750 }
751}
752\f
24e3d3bf
JB
753/* Find the interval in TREE corresponding to the relative position
754 FROM and delete as much as possible of AMOUNT from that interval.
755 Return the amount actually deleted, and if the interval was
756 zeroed-out, delete that interval node from the tree.
757
758 Note that FROM is actually origin zero, aka relative to the
759 leftmost edge of tree. This is appropriate since we call ourselves
760 recursively on subtrees.
a50699fd 761
1d1d7ba0
JA
762 Do this by recursing down TREE to the interval in question, and
763 deleting the appropriate amount of text. */
a50699fd
JA
764
765static int
766interval_deletion_adjustment (tree, from, amount)
767 register INTERVAL tree;
768 register int from, amount;
769{
770 register int relative_position = from;
771
772 if (NULL_INTERVAL_P (tree))
773 return 0;
774
775 /* Left branch */
24e3d3bf 776 if (relative_position < LEFT_TOTAL_LENGTH (tree))
a50699fd
JA
777 {
778 int subtract = interval_deletion_adjustment (tree->left,
779 relative_position,
780 amount);
781 tree->total_length -= subtract;
782 return subtract;
783 }
784 /* Right branch */
24e3d3bf
JB
785 else if (relative_position >= (TOTAL_LENGTH (tree)
786 - RIGHT_TOTAL_LENGTH (tree)))
a50699fd
JA
787 {
788 int subtract;
789
790 relative_position -= (tree->total_length
791 - RIGHT_TOTAL_LENGTH (tree));
792 subtract = interval_deletion_adjustment (tree->right,
793 relative_position,
794 amount);
795 tree->total_length -= subtract;
796 return subtract;
797 }
798 /* Here -- this node */
799 else
800 {
24e3d3bf
JB
801 /* How much can we delete from this interval? */
802 int my_amount = ((tree->total_length
803 - RIGHT_TOTAL_LENGTH (tree))
804 - relative_position);
805
806 if (amount > my_amount)
807 amount = my_amount;
808
809 tree->total_length -= amount;
810 if (LENGTH (tree) == 0)
811 delete_interval (tree);
812
813 return amount;
a50699fd
JA
814 }
815
1d1d7ba0 816 /* Never reach here */
a50699fd
JA
817}
818
24e3d3bf
JB
819/* Effect the adjustments necessary to the interval tree of BUFFER to
820 correspond to the deletion of LENGTH characters from that buffer
821 text. The deletion is effected at position START (which is a
822 buffer position, i.e. origin 1). */
1d1d7ba0 823
a50699fd
JA
824static void
825adjust_intervals_for_deletion (buffer, start, length)
826 struct buffer *buffer;
827 int start, length;
828{
829 register int left_to_delete = length;
830 register INTERVAL tree = buffer->intervals;
831 register int deleted;
832
833 if (NULL_INTERVAL_P (tree))
834 return;
835
24e3d3bf
JB
836 if (start > BEG + TOTAL_LENGTH (tree)
837 || start + length > BEG + TOTAL_LENGTH (tree))
838 abort ();
839
a50699fd
JA
840 if (length == TOTAL_LENGTH (tree))
841 {
842 buffer->intervals = NULL_INTERVAL;
843 return;
844 }
845
846 if (ONLY_INTERVAL_P (tree))
847 {
848 tree->total_length -= length;
849 return;
850 }
851
24e3d3bf
JB
852 if (start > BEG + TOTAL_LENGTH (tree))
853 start = BEG + TOTAL_LENGTH (tree);
a50699fd
JA
854 while (left_to_delete > 0)
855 {
24e3d3bf 856 left_to_delete -= interval_deletion_adjustment (tree, start - 1,
a50699fd
JA
857 left_to_delete);
858 tree = buffer->intervals;
859 if (left_to_delete == tree->total_length)
860 {
861 buffer->intervals = NULL_INTERVAL;
862 return;
863 }
864 }
865}
866\f
eb8c3be9 867/* Make the adjustments necessary to the interval tree of BUFFER to
1d1d7ba0
JA
868 represent an addition or deletion of LENGTH characters starting
869 at position START. Addition or deletion is indicated by the sign
870 of LENGTH. */
a50699fd
JA
871
872INLINE void
873offset_intervals (buffer, start, length)
874 struct buffer *buffer;
875 int start, length;
876{
877 if (NULL_INTERVAL_P (buffer->intervals) || length == 0)
878 return;
879
880 if (length > 0)
881 adjust_intervals_for_insertion (buffer->intervals, start, length);
882 else
883 adjust_intervals_for_deletion (buffer, start, -length);
884}
9c79dd1b
JA
885\f
886/* Merge interval I with its lexicographic successor. The resulting
887 interval is returned, and has the properties of the original
888 successor. The properties of I are lost. I is removed from the
889 interval tree.
890
891 IMPORTANT:
892 The caller must verify that this is not the last (rightmost)
893 interval. */
894
895INTERVAL
896merge_interval_right (i)
897 register INTERVAL i;
898{
899 register int absorb = LENGTH (i);
900 register INTERVAL successor;
901
902 /* Zero out this interval. */
903 i->total_length -= absorb;
904
905 /* Find the succeeding interval. */
906 if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb
907 as we descend. */
908 {
909 successor = i->right;
910 while (! NULL_LEFT_CHILD (successor))
911 {
912 successor->total_length += absorb;
913 successor = successor->left;
914 }
915
916 successor->total_length += absorb;
917 delete_interval (i);
918 return successor;
919 }
920
921 successor = i;
922 while (! NULL_PARENT (successor)) /* It's above us. Subtract as
923 we ascend. */
924 {
925 if (AM_LEFT_CHILD (successor))
926 {
927 successor = successor->parent;
928 delete_interval (i);
929 return successor;
930 }
931
932 successor = successor->parent;
933 successor->total_length -= absorb;
934 }
935
936 /* This must be the rightmost or last interval and cannot
937 be merged right. The caller should have known. */
938 abort ();
939}
940\f
941/* Merge interval I with its lexicographic predecessor. The resulting
942 interval is returned, and has the properties of the original predecessor.
943 The properties of I are lost. Interval node I is removed from the tree.
944
945 IMPORTANT:
946 The caller must verify that this is not the first (leftmost) interval. */
947
948INTERVAL
949merge_interval_left (i)
950 register INTERVAL i;
951{
952 register int absorb = LENGTH (i);
953 register INTERVAL predecessor;
954
955 /* Zero out this interval. */
956 i->total_length -= absorb;
957
958 /* Find the preceding interval. */
959 if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down,
960 adding ABSORB as we go. */
961 {
962 predecessor = i->left;
963 while (! NULL_RIGHT_CHILD (predecessor))
964 {
965 predecessor->total_length += absorb;
966 predecessor = predecessor->right;
967 }
968
969 predecessor->total_length += absorb;
970 delete_interval (i);
971 return predecessor;
972 }
973
974 predecessor = i;
975 while (! NULL_PARENT (predecessor)) /* It's above us. Go up,
976 subtracting ABSORB. */
977 {
978 if (AM_RIGHT_CHILD (predecessor))
979 {
980 predecessor = predecessor->parent;
981 delete_interval (i);
982 return predecessor;
983 }
984
985 predecessor = predecessor->parent;
986 predecessor->total_length -= absorb;
987 }
a50699fd 988
9c79dd1b
JA
989 /* This must be the leftmost or first interval and cannot
990 be merged left. The caller should have known. */
991 abort ();
992}
993\f
1d1d7ba0
JA
994/* Make an exact copy of interval tree SOURCE which descends from
995 PARENT. This is done by recursing through SOURCE, copying
996 the current interval and its properties, and then adjusting
997 the pointers of the copy. */
998
a50699fd
JA
999static INTERVAL
1000reproduce_tree (source, parent)
1001 INTERVAL source, parent;
1002{
1003 register INTERVAL t = make_interval ();
1004
1005 bcopy (source, t, INTERVAL_SIZE);
1006 copy_properties (source, t);
1007 t->parent = parent;
1008 if (! NULL_LEFT_CHILD (source))
1009 t->left = reproduce_tree (source->left, t);
1010 if (! NULL_RIGHT_CHILD (source))
1011 t->right = reproduce_tree (source->right, t);
1012
1013 return t;
1014}
1015
24e3d3bf
JB
1016#if 0
1017/* Nobody calls this. Perhaps it's a vestige of an earlier design. */
1018
1d1d7ba0
JA
1019/* Make a new interval of length LENGTH starting at START in the
1020 group of intervals INTERVALS, which is actually an interval tree.
1021 Returns the new interval.
1022
1023 Generate an error if the new positions would overlap an existing
1024 interval. */
1025
a50699fd
JA
1026static INTERVAL
1027make_new_interval (intervals, start, length)
1028 INTERVAL intervals;
1029 int start, length;
1030{
1031 INTERVAL slot;
1032
1033 slot = find_interval (intervals, start);
1034 if (start + length > slot->position + LENGTH (slot))
1035 error ("Interval would overlap");
1036
1037 if (start == slot->position && length == LENGTH (slot))
1038 return slot;
1039
1040 if (slot->position == start)
1041 {
1042 /* New right node. */
1043 split_interval_right (slot, length + 1);
1044 return slot;
1045 }
1046
1047 if (slot->position + LENGTH (slot) == start + length)
1048 {
1049 /* New left node. */
1050 split_interval_left (slot, LENGTH (slot) - length + 1);
1051 return slot;
1052 }
1053
1054 /* Convert interval SLOT into three intervals. */
1055 split_interval_left (slot, start - slot->position + 1);
1056 split_interval_right (slot, length + 1);
1057 return slot;
1058}
24e3d3bf 1059#endif
294efdbe 1060\f
9c79dd1b 1061/* Insert the intervals of SOURCE into BUFFER at POSITION.
a50699fd
JA
1062
1063 This is used in insdel.c when inserting Lisp_Strings into
9c79dd1b 1064 the buffer. The text corresponding to SOURCE is already in
a50699fd
JA
1065 the buffer when this is called. The intervals of new tree are
1066 those belonging to the string being inserted; a copy is not made.
1067
1068 If the inserted text had no intervals associated, this function
1069 simply returns -- offset_intervals should handle placing the
90ba40fc 1070 text in the correct interval, depending on the sticky bits.
a50699fd
JA
1071
1072 If the inserted text had properties (intervals), then there are two
1073 cases -- either insertion happened in the middle of some interval,
1074 or between two intervals.
1075
1076 If the text goes into the middle of an interval, then new
1077 intervals are created in the middle with only the properties of
1078 the new text, *unless* the macro MERGE_INSERTIONS is true, in
1079 which case the new text has the union of its properties and those
1080 of the text into which it was inserted.
1081
1082 If the text goes between two intervals, then if neither interval
90ba40fc
JA
1083 had its appropriate sticky property set (front_sticky, rear_sticky),
1084 the new text has only its properties. If one of the sticky properties
a50699fd 1085 is set, then the new text "sticks" to that region and its properties
eb8c3be9 1086 depend on merging as above. If both the preceding and succeeding
90ba40fc
JA
1087 intervals to the new text are "sticky", then the new text retains
1088 only its properties, as if neither sticky property were set. Perhaps
a50699fd
JA
1089 we should consider merging all three sets of properties onto the new
1090 text... */
1091
1092void
9c79dd1b
JA
1093graft_intervals_into_buffer (source, position, buffer)
1094 INTERVAL source;
a50699fd 1095 int position;
9c79dd1b 1096 struct buffer *buffer;
a50699fd 1097{
323a7ad4 1098 register INTERVAL under, over, this, prev;
9c79dd1b 1099 register INTERVAL tree = buffer->intervals;
323a7ad4 1100 int middle;
a50699fd
JA
1101
1102 /* If the new text has no properties, it becomes part of whatever
323a7ad4 1103 interval it was inserted into. */
9c79dd1b 1104 if (NULL_INTERVAL_P (source))
a50699fd
JA
1105 return;
1106
a50699fd
JA
1107 if (NULL_INTERVAL_P (tree))
1108 {
1109 /* The inserted text constitutes the whole buffer, so
1110 simply copy over the interval structure. */
249a6da9 1111 if (BUF_Z (buffer) == TOTAL_LENGTH (source))
a50699fd 1112 {
9c79dd1b 1113 buffer->intervals = reproduce_tree (source, tree->parent);
a50699fd
JA
1114 /* Explicitly free the old tree here. */
1115
1116 return;
1117 }
1118
1119 /* Create an interval tree in which to place a copy
323a7ad4 1120 of the intervals of the inserted string. */
a50699fd 1121 {
249a6da9
JA
1122 Lisp_Object buf;
1123 XSET (buf, Lisp_Buffer, buffer);
323a7ad4 1124 tree = create_root_interval (buf);
a50699fd
JA
1125 }
1126 }
1127 else
9c79dd1b 1128 if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (source))
323a7ad4
RS
1129 /* If the buffer contains only the new string, but
1130 there was already some interval tree there, then it may be
1131 some zero length intervals. Eventually, do something clever
1132 about inserting properly. For now, just waste the old intervals. */
1133 {
1134 buffer->intervals = reproduce_tree (source, tree->parent);
1135 /* Explicitly free the old tree here. */
a50699fd 1136
323a7ad4
RS
1137 return;
1138 }
1139 else
1140 /* Paranoia -- the text has already been added, so this buffer
1141 should be of non-zero length. */
1142 if (TOTAL_LENGTH (tree) == 0)
1143 abort ();
a50699fd
JA
1144
1145 this = under = find_interval (tree, position);
1146 if (NULL_INTERVAL_P (under)) /* Paranoia */
1147 abort ();
9c79dd1b 1148 over = find_interval (source, 1);
a50699fd 1149
323a7ad4
RS
1150 /* Here for insertion in the middle of an interval.
1151 Split off an equivalent interval to the right,
1152 then don't bother with it any more. */
a50699fd 1153
323a7ad4 1154 if (position > under->position)
a50699fd
JA
1155 {
1156 INTERVAL end_unchanged
323a7ad4 1157 = split_interval_left (this, position - under->position + 1);
a50699fd 1158 copy_properties (under, end_unchanged);
323a7ad4
RS
1159 under->position = position;
1160 prev = 0;
1161 middle = 1;
a50699fd 1162 }
323a7ad4
RS
1163 else
1164 {
1165 prev = previous_interval (under);
1166 if (prev && !END_STICKY_P (prev))
1167 prev = 0;
1168 }
1169
1170 /* Insertion is now at beginning of UNDER. */
a50699fd 1171
323a7ad4
RS
1172 /* The inserted text "sticks" to the interval `under',
1173 which means it gets those properties. */
a50699fd
JA
1174 while (! NULL_INTERVAL_P (over))
1175 {
323a7ad4
RS
1176 position = LENGTH (over) + 1;
1177 if (position < LENGTH (under))
1178 this = split_interval_left (under, position);
1179 else
1180 this = under;
a50699fd 1181 copy_properties (over, this);
323a7ad4
RS
1182 /* Insertion at the end of an interval, PREV,
1183 inherits from PREV if PREV is sticky at the end. */
1184 if (prev && ! FRONT_STICKY_P (under)
1185 && MERGE_INSERTIONS (prev))
1186 merge_properties (prev, this);
1187 /* Maybe it inherits from the following interval
1188 if that is sticky at the front. */
1189 else if ((FRONT_STICKY_P (under) || middle)
1190 && MERGE_INSERTIONS (under))
a50699fd 1191 merge_properties (under, this);
a50699fd
JA
1192 over = next_interval (over);
1193 }
1194
9c79dd1b 1195 buffer->intervals = balance_intervals (buffer->intervals);
a50699fd
JA
1196 return;
1197}
1198
5cae0ec6
RS
1199/* Get the value of property PROP from PLIST,
1200 which is the plist of an interval.
1201 We check for direct properties and for categories with property PROP. */
1202
1203Lisp_Object
323a7ad4
RS
1204textget (plist, prop)
1205 Lisp_Object plist;
1206 register Lisp_Object prop;
1207{
5cae0ec6
RS
1208 register Lisp_Object tail, fallback;
1209 fallback = Qnil;
323a7ad4
RS
1210
1211 for (tail = plist; !NILP (tail); tail = Fcdr (Fcdr (tail)))
1212 {
1213 register Lisp_Object tem;
1214 tem = Fcar (tail);
1215 if (EQ (prop, tem))
1216 return Fcar (Fcdr (tail));
5cae0ec6
RS
1217 if (EQ (tem, Qcategory))
1218 fallback = Fget (Fcar (Fcdr (tail)), prop);
323a7ad4 1219 }
5cae0ec6
RS
1220
1221 return fallback;
323a7ad4 1222}
294efdbe 1223\f
5cae0ec6
RS
1224/* Set point in BUFFER to POSITION. If the target position is
1225 before an invisible character which is not displayed with a special glyph,
323a7ad4 1226 move back to an ok place to display. */
a50699fd
JA
1227
1228void
1229set_point (position, buffer)
1230 register int position;
1231 register struct buffer *buffer;
1232{
323a7ad4 1233 register INTERVAL to, from, toprev, fromprev, target;
a50699fd
JA
1234 int buffer_point;
1235 register Lisp_Object obj;
1236 int backwards = (position < BUF_PT (buffer)) ? 1 : 0;
9c79dd1b 1237 int old_position = buffer->text.pt;
a50699fd
JA
1238
1239 if (position == buffer->text.pt)
1240 return;
1241
62056764
JB
1242 /* Check this now, before checking if the buffer has any intervals.
1243 That way, we can catch conditions which break this sanity check
1244 whether or not there are intervals in the buffer. */
1245 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer))
1246 abort ();
1247
a50699fd
JA
1248 if (NULL_INTERVAL_P (buffer->intervals))
1249 {
1250 buffer->text.pt = position;
1251 return;
1252 }
1253
323a7ad4
RS
1254 /* Set TO to the interval containing the char after POSITION,
1255 and TOPREV to the interval containing the char before POSITION.
1256 Either one may be null. They may be equal. */
24e3d3bf 1257 to = find_interval (buffer->intervals, position);
294efdbe
RS
1258 if (position == BUF_BEGV (buffer))
1259 toprev = 0;
1260 else if (to->position == position)
323a7ad4 1261 toprev = previous_interval (to);
323a7ad4
RS
1262 else
1263 toprev = to;
1264
294efdbe
RS
1265 buffer_point = (BUF_PT (buffer) == BUF_ZV (buffer)
1266 ? BUF_ZV (buffer) - 1
323a7ad4 1267 : BUF_PT (buffer));
9c79dd1b 1268
323a7ad4
RS
1269 /* Set FROM to the interval containing the char after PT,
1270 and FROMPREV to the interval containing the char before PT.
1271 Either one may be null. They may be equal. */
9c79dd1b 1272 /* We could cache this and save time. */
a50699fd 1273 from = find_interval (buffer->intervals, buffer_point);
294efdbe
RS
1274 if (from->position == BUF_BEGV (buffer))
1275 fromprev = 0;
1276 else if (from->position == BUF_PT (buffer))
323a7ad4
RS
1277 fromprev = previous_interval (from);
1278 else if (buffer_point != BUF_PT (buffer))
1279 fromprev = from, from = 0;
1280 else
1281 fromprev = from;
a50699fd
JA
1282
1283 /* Moving within an interval */
323a7ad4 1284 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to))
a50699fd
JA
1285 {
1286 buffer->text.pt = position;
1287 return;
1288 }
1289
5cae0ec6
RS
1290 /* If the new position is before an invisible character,
1291 move forward over all such. */
1292 while (! NULL_INTERVAL_P (to)
1293 && ! INTERVAL_VISIBLE_P (to)
1294 && ! DISPLAY_INVISIBLE_GLYPH (to))
a50699fd 1295 {
5cae0ec6
RS
1296 toprev = to;
1297 to = next_interval (to);
0df8950e
RS
1298 if (NULL_INTERVAL_P (to))
1299 position = BUF_ZV (buffer);
1300 else
1301 position = to->position;
a50699fd 1302 }
323a7ad4
RS
1303
1304 buffer->text.pt = position;
a50699fd 1305
d7e3e52b
JA
1306 /* We run point-left and point-entered hooks here, iff the
1307 two intervals are not equivalent. These hooks take
323a7ad4
RS
1308 (old_point, new_point) as arguments. */
1309 if (! intervals_equal (from, to)
1310 || ! intervals_equal (fromprev, toprev))
9c79dd1b 1311 {
323a7ad4
RS
1312 Lisp_Object leave_after, leave_before, enter_after, enter_before;
1313
1314 if (fromprev)
1315 leave_after = textget (fromprev->plist, Qpoint_left);
1316 else
1317 leave_after = Qnil;
1318 if (from)
1319 leave_before = textget (from->plist, Qpoint_left);
1320 else
1321 leave_before = Qnil;
1322
1323 if (toprev)
1324 enter_after = textget (toprev->plist, Qpoint_entered);
1325 else
1326 enter_after = Qnil;
1327 if (to)
1328 enter_before = textget (to->plist, Qpoint_entered);
1329 else
1330 enter_before = Qnil;
9c79dd1b 1331
323a7ad4
RS
1332 if (! EQ (leave_before, enter_before) && !NILP (leave_before))
1333 call2 (leave_before, old_position, position);
1334 if (! EQ (leave_after, enter_after) && !NILP (leave_after))
1335 call2 (leave_after, old_position, position);
9c79dd1b 1336
323a7ad4
RS
1337 if (! EQ (enter_before, leave_before) && !NILP (enter_before))
1338 call2 (enter_before, old_position, position);
1339 if (! EQ (enter_after, leave_after) && !NILP (enter_after))
1340 call2 (enter_after, old_position, position);
9c79dd1b 1341 }
a50699fd
JA
1342}
1343
9c79dd1b 1344/* Set point temporarily, without checking any text properties. */
a50699fd 1345
9c79dd1b
JA
1346INLINE void
1347temp_set_point (position, buffer)
1348 int position;
1349 struct buffer *buffer;
1350{
1351 buffer->text.pt = position;
1352}
294efdbe 1353\f
5cae0ec6
RS
1354/* Return the proper local map for position POSITION in BUFFER.
1355 Use the map specified by the local-map property, if any.
1356 Otherwise, use BUFFER's local map. */
1357
1358Lisp_Object
1359get_local_map (position, buffer)
1360 register int position;
1361 register struct buffer *buffer;
1362{
1363 register INTERVAL interval;
1364 Lisp_Object prop, tem;
1365
1366 if (NULL_INTERVAL_P (buffer->intervals))
1367 return current_buffer->keymap;
1368
1369 /* Perhaps we should just change `position' to the limit. */
1370 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer))
1371 abort ();
1372
5cae0ec6
RS
1373 interval = find_interval (buffer->intervals, position);
1374 prop = textget (interval->plist, Qlocal_map);
1375 if (NILP (prop))
1376 return current_buffer->keymap;
1377
1378 /* Use the local map only if it is valid. */
1379 tem = Fkeymapp (prop);
1380 if (!NILP (tem))
1381 return prop;
1382
1383 return current_buffer->keymap;
1384}
1385\f
294efdbe
RS
1386/* Call the modification hook functions in LIST, each with START and END. */
1387
1388static void
1389call_mod_hooks (list, start, end)
1390 Lisp_Object list, start, end;
1391{
1392 struct gcpro gcpro1;
1393 GCPRO1 (list);
1394 while (!NILP (list))
1395 {
1396 call2 (Fcar (list), start, end);
1397 list = Fcdr (list);
1398 }
1399 UNGCPRO;
1400}
9c79dd1b
JA
1401
1402/* Check for read-only intervals and signal an error if we find one.
1403 Then check for any modification hooks in the range START up to
1404 (but not including) TO. Create a list of all these hooks in
1405 lexicographic order, eliminating consecutive extra copies of the
1406 same hook. Then call those hooks in order, with START and END - 1
1407 as arguments. */
a50699fd
JA
1408
1409void
1410verify_interval_modification (buf, start, end)
1411 struct buffer *buf;
1412 int start, end;
1413{
1414 register INTERVAL intervals = buf->intervals;
294efdbe
RS
1415 register INTERVAL i, prev;
1416 Lisp_Object hooks;
1417 register Lisp_Object prev_mod_hooks;
1418 Lisp_Object mod_hooks;
9c79dd1b 1419 struct gcpro gcpro1;
a50699fd 1420
294efdbe
RS
1421 hooks = Qnil;
1422 prev_mod_hooks = Qnil;
1423 mod_hooks = Qnil;
1424
a50699fd
JA
1425 if (NULL_INTERVAL_P (intervals))
1426 return;
1427
1428 if (start > end)
1429 {
1430 int temp = start;
1431 start = end;
1432 end = temp;
1433 }
1434
294efdbe
RS
1435 /* For an insert operation, check the two chars around the position. */
1436 if (start == end)
a50699fd 1437 {
294efdbe
RS
1438 INTERVAL prev;
1439 Lisp_Object before, after;
a50699fd 1440
294efdbe
RS
1441 /* Set I to the interval containing the char after START,
1442 and PREV to the interval containing the char before START.
1443 Either one may be null. They may be equal. */
24e3d3bf 1444 i = find_interval (intervals, start);
294efdbe
RS
1445
1446 if (start == BUF_BEGV (buf))
1447 prev = 0;
1448 if (i->position == start)
1449 prev = previous_interval (i);
1450 else if (i->position < start)
1451 prev = i;
1452 if (start == BUF_ZV (buf))
1453 i = 0;
1454
1455 if (NULL_INTERVAL_P (prev))
1456 {
7c92db56 1457 if (! INTERVAL_WRITABLE_P (i))
294efdbe
RS
1458 error ("Attempt to insert within read-only text");
1459 }
1460 else if (NULL_INTERVAL_P (i))
1461 {
7c92db56 1462 if (! INTERVAL_WRITABLE_P (prev))
294efdbe
RS
1463 error ("Attempt to insert within read-only text");
1464 }
1465 else
1466 {
5cae0ec6
RS
1467 before = textget (prev->plist, Qread_only);
1468 after = textget (i->plist, Qread_only);
7c92db56
RS
1469 if (! NILP (before) && EQ (before, after)
1470 /* This checks Vinhibit_read_only properly
1471 for the common value of the read-only property. */
1472 && ! INTERVAL_WRITABLE_P (i))
294efdbe
RS
1473 error ("Attempt to insert within read-only text");
1474 }
1475
c3649419 1476 /* Run both insert hooks (just once if they're the same). */
294efdbe 1477 if (!NULL_INTERVAL_P (prev))
f1ca9012 1478 prev_mod_hooks = textget (prev->plist, Qinsert_behind_hooks);
294efdbe 1479 if (!NULL_INTERVAL_P (i))
f1ca9012 1480 mod_hooks = textget (i->plist, Qinsert_in_front_hooks);
294efdbe
RS
1481 GCPRO1 (mod_hooks);
1482 if (! NILP (prev_mod_hooks))
1483 call_mod_hooks (prev_mod_hooks, make_number (start),
1484 make_number (end));
1485 UNGCPRO;
1486 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1487 call_mod_hooks (mod_hooks, make_number (start), make_number (end));
a50699fd
JA
1488 }
1489 else
a50699fd 1490 {
294efdbe
RS
1491 /* Loop over intervals on or next to START...END,
1492 collecting their hooks. */
9c79dd1b 1493
294efdbe
RS
1494 i = find_interval (intervals, start);
1495 do
9c79dd1b 1496 {
294efdbe
RS
1497 if (! INTERVAL_WRITABLE_P (i))
1498 error ("Attempt to modify read-only text");
9c79dd1b 1499
294efdbe
RS
1500 mod_hooks = textget (i->plist, Qmodification_hooks);
1501 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1502 {
1503 hooks = Fcons (mod_hooks, hooks);
1504 prev_mod_hooks = mod_hooks;
1505 }
a50699fd 1506
294efdbe
RS
1507 i = next_interval (i);
1508 }
1509 /* Keep going thru the interval containing the char before END. */
1510 while (! NULL_INTERVAL_P (i) && i->position < end);
1511
1512 GCPRO1 (hooks);
1513 hooks = Fnreverse (hooks);
1514 while (! EQ (hooks, Qnil))
1515 {
1516 call_mod_hooks (Fcar (hooks), make_number (start),
1517 make_number (end));
1518 hooks = Fcdr (hooks);
1519 }
1520 UNGCPRO;
9c79dd1b 1521 }
a50699fd
JA
1522}
1523
1524/* Balance an interval node if the amount of text in its left and right
1525 subtrees differs by more than the percentage specified by
1526 `interval-balance-threshold'. */
1527
1528static INTERVAL
1529balance_an_interval (i)
1530 INTERVAL i;
1531{
1532 register int total_children_size = (LEFT_TOTAL_LENGTH (i)
1533 + RIGHT_TOTAL_LENGTH (i));
1534 register int threshold = (XFASTINT (interval_balance_threshold)
1535 * (total_children_size / 100));
1536
95e3e1ef
RS
1537 /* Balance within each side. */
1538 balance_intervals (i->left);
1539 balance_intervals (i->right);
a50699fd
JA
1540
1541 if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
1542 && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
95e3e1ef
RS
1543 {
1544 i = rotate_right (i);
1545 /* If that made it unbalanced the other way, take it back. */
1546 if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
1547 && (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
1548 return rotate_left (i);
1549 return i;
1550 }
a50699fd 1551
95e3e1ef
RS
1552 if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
1553 && (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
1554 {
1555 i = rotate_left (i);
1556 if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
1557 && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
1558 return rotate_right (i);
1559 return i;
1560 }
a50699fd
JA
1561
1562 return i;
1563}
1564
1565/* Balance the interval tree TREE. Balancing is by weight
1566 (the amount of text). */
1567
1568INTERVAL
1569balance_intervals (tree)
1570 register INTERVAL tree;
1571{
1572 register INTERVAL new_tree;
1573
1574 if (NULL_INTERVAL_P (tree))
1575 return NULL_INTERVAL;
1576
1577 new_tree = tree;
1578 do
1579 {
1580 tree = new_tree;
1581 new_tree = balance_an_interval (new_tree);
1582 }
1583 while (new_tree != tree);
1584
1585 return new_tree;
1586}
1587
9c79dd1b 1588/* Produce an interval tree reflecting the intervals in
a50699fd
JA
1589 TREE from START to START + LENGTH. */
1590
7b1d5b85 1591INTERVAL
a50699fd
JA
1592copy_intervals (tree, start, length)
1593 INTERVAL tree;
1594 int start, length;
1595{
1596 register INTERVAL i, new, t;
95e3e1ef 1597 register int got, prevlen;
a50699fd
JA
1598
1599 if (NULL_INTERVAL_P (tree) || length <= 0)
1600 return NULL_INTERVAL;
1601
1602 i = find_interval (tree, start);
1603 if (NULL_INTERVAL_P (i) || LENGTH (i) == 0)
1604 abort ();
1605
1606 /* If there is only one interval and it's the default, return nil. */
1607 if ((start - i->position + 1 + length) < LENGTH (i)
1608 && DEFAULT_INTERVAL_P (i))
1609 return NULL_INTERVAL;
1610
1611 new = make_interval ();
1612 new->position = 1;
1613 got = (LENGTH (i) - (start - i->position));
9c79dd1b 1614 new->total_length = length;
a50699fd
JA
1615 copy_properties (i, new);
1616
1617 t = new;
95e3e1ef 1618 prevlen = got;
a50699fd
JA
1619 while (got < length)
1620 {
1621 i = next_interval (i);
95e3e1ef 1622 t = split_interval_right (t, prevlen + 1);
a50699fd 1623 copy_properties (i, t);
95e3e1ef
RS
1624 prevlen = LENGTH (i);
1625 got += prevlen;
a50699fd
JA
1626 }
1627
a50699fd
JA
1628 return balance_intervals (new);
1629}
1630
a50699fd
JA
1631/* Give STRING the properties of BUFFER from POSITION to LENGTH. */
1632
d7e3e52b 1633INLINE void
a50699fd
JA
1634copy_intervals_to_string (string, buffer, position, length)
1635 Lisp_Object string, buffer;
1636 int position, length;
1637{
1638 INTERVAL interval_copy = copy_intervals (XBUFFER (buffer)->intervals,
1639 position, length);
1640 if (NULL_INTERVAL_P (interval_copy))
1641 return;
1642
1643 interval_copy->parent = (INTERVAL) string;
1644 XSTRING (string)->intervals = interval_copy;
1645}
d2f7a802
JA
1646
1647#endif /* USE_TEXT_PROPERTIES */