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