(map-y-or-n-p): If LIST is nil, just return.
[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 {
9c79dd1b 1117 buffer->intervals = reproduce_tree (source, tree->parent);
a50699fd
JA
1118 /* Explicitly free the old tree here. */
1119
1120 return;
1121 }
1122
1123 /* Create an interval tree in which to place a copy
323a7ad4 1124 of the intervals of the inserted string. */
a50699fd 1125 {
249a6da9
JA
1126 Lisp_Object buf;
1127 XSET (buf, Lisp_Buffer, buffer);
323a7ad4 1128 tree = create_root_interval (buf);
a50699fd
JA
1129 }
1130 }
1131 else
9c79dd1b 1132 if (TOTAL_LENGTH (tree) == TOTAL_LENGTH (source))
323a7ad4
RS
1133 /* If the buffer contains only the new string, but
1134 there was already some interval tree there, then it may be
1135 some zero length intervals. Eventually, do something clever
1136 about inserting properly. For now, just waste the old intervals. */
1137 {
1138 buffer->intervals = reproduce_tree (source, tree->parent);
1139 /* Explicitly free the old tree here. */
a50699fd 1140
323a7ad4
RS
1141 return;
1142 }
1143 else
1144 /* Paranoia -- the text has already been added, so this buffer
1145 should be of non-zero length. */
1146 if (TOTAL_LENGTH (tree) == 0)
1147 abort ();
a50699fd
JA
1148
1149 this = under = find_interval (tree, position);
1150 if (NULL_INTERVAL_P (under)) /* Paranoia */
1151 abort ();
9c79dd1b 1152 over = find_interval (source, 1);
a50699fd 1153
323a7ad4
RS
1154 /* Here for insertion in the middle of an interval.
1155 Split off an equivalent interval to the right,
1156 then don't bother with it any more. */
a50699fd 1157
323a7ad4 1158 if (position > under->position)
a50699fd
JA
1159 {
1160 INTERVAL end_unchanged
2bc7a79b 1161 = split_interval_left (this, position - under->position);
a50699fd 1162 copy_properties (under, end_unchanged);
323a7ad4
RS
1163 under->position = position;
1164 prev = 0;
1165 middle = 1;
a50699fd 1166 }
323a7ad4
RS
1167 else
1168 {
1169 prev = previous_interval (under);
1170 if (prev && !END_STICKY_P (prev))
1171 prev = 0;
1172 }
1173
1174 /* Insertion is now at beginning of UNDER. */
a50699fd 1175
323a7ad4
RS
1176 /* The inserted text "sticks" to the interval `under',
1177 which means it gets those properties. */
a50699fd
JA
1178 while (! NULL_INTERVAL_P (over))
1179 {
2bc7a79b
JB
1180 if (LENGTH (over) + 1 < LENGTH (under))
1181 this = split_interval_left (under, LENGTH (over));
323a7ad4
RS
1182 else
1183 this = under;
a50699fd 1184 copy_properties (over, this);
323a7ad4
RS
1185 /* Insertion at the end of an interval, PREV,
1186 inherits from PREV if PREV is sticky at the end. */
1187 if (prev && ! FRONT_STICKY_P (under)
1188 && MERGE_INSERTIONS (prev))
1189 merge_properties (prev, this);
1190 /* Maybe it inherits from the following interval
1191 if that is sticky at the front. */
1192 else if ((FRONT_STICKY_P (under) || middle)
1193 && MERGE_INSERTIONS (under))
a50699fd 1194 merge_properties (under, this);
a50699fd
JA
1195 over = next_interval (over);
1196 }
1197
9c79dd1b 1198 buffer->intervals = balance_intervals (buffer->intervals);
a50699fd
JA
1199 return;
1200}
1201
5cae0ec6
RS
1202/* Get the value of property PROP from PLIST,
1203 which is the plist of an interval.
1204 We check for direct properties and for categories with property PROP. */
1205
1206Lisp_Object
323a7ad4
RS
1207textget (plist, prop)
1208 Lisp_Object plist;
1209 register Lisp_Object prop;
1210{
5cae0ec6
RS
1211 register Lisp_Object tail, fallback;
1212 fallback = Qnil;
323a7ad4
RS
1213
1214 for (tail = plist; !NILP (tail); tail = Fcdr (Fcdr (tail)))
1215 {
1216 register Lisp_Object tem;
1217 tem = Fcar (tail);
1218 if (EQ (prop, tem))
1219 return Fcar (Fcdr (tail));
5cae0ec6
RS
1220 if (EQ (tem, Qcategory))
1221 fallback = Fget (Fcar (Fcdr (tail)), prop);
323a7ad4 1222 }
5cae0ec6
RS
1223
1224 return fallback;
323a7ad4 1225}
294efdbe 1226\f
5cae0ec6
RS
1227/* Set point in BUFFER to POSITION. If the target position is
1228 before an invisible character which is not displayed with a special glyph,
323a7ad4 1229 move back to an ok place to display. */
a50699fd
JA
1230
1231void
1232set_point (position, buffer)
1233 register int position;
1234 register struct buffer *buffer;
1235{
323a7ad4 1236 register INTERVAL to, from, toprev, fromprev, target;
a50699fd
JA
1237 int buffer_point;
1238 register Lisp_Object obj;
1239 int backwards = (position < BUF_PT (buffer)) ? 1 : 0;
9c79dd1b 1240 int old_position = buffer->text.pt;
a50699fd
JA
1241
1242 if (position == buffer->text.pt)
1243 return;
1244
62056764
JB
1245 /* Check this now, before checking if the buffer has any intervals.
1246 That way, we can catch conditions which break this sanity check
1247 whether or not there are intervals in the buffer. */
1248 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer))
1249 abort ();
1250
a50699fd
JA
1251 if (NULL_INTERVAL_P (buffer->intervals))
1252 {
1253 buffer->text.pt = position;
1254 return;
1255 }
1256
323a7ad4
RS
1257 /* Set TO to the interval containing the char after POSITION,
1258 and TOPREV to the interval containing the char before POSITION.
1259 Either one may be null. They may be equal. */
24e3d3bf 1260 to = find_interval (buffer->intervals, position);
294efdbe
RS
1261 if (position == BUF_BEGV (buffer))
1262 toprev = 0;
1263 else if (to->position == position)
323a7ad4 1264 toprev = previous_interval (to);
323a7ad4
RS
1265 else
1266 toprev = to;
1267
294efdbe
RS
1268 buffer_point = (BUF_PT (buffer) == BUF_ZV (buffer)
1269 ? BUF_ZV (buffer) - 1
323a7ad4 1270 : BUF_PT (buffer));
9c79dd1b 1271
323a7ad4
RS
1272 /* Set FROM to the interval containing the char after PT,
1273 and FROMPREV to the interval containing the char before PT.
1274 Either one may be null. They may be equal. */
9c79dd1b 1275 /* We could cache this and save time. */
a50699fd 1276 from = find_interval (buffer->intervals, buffer_point);
294efdbe
RS
1277 if (from->position == BUF_BEGV (buffer))
1278 fromprev = 0;
1279 else if (from->position == BUF_PT (buffer))
323a7ad4
RS
1280 fromprev = previous_interval (from);
1281 else if (buffer_point != BUF_PT (buffer))
1282 fromprev = from, from = 0;
1283 else
1284 fromprev = from;
a50699fd
JA
1285
1286 /* Moving within an interval */
323a7ad4 1287 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to))
a50699fd
JA
1288 {
1289 buffer->text.pt = position;
1290 return;
1291 }
1292
5cae0ec6
RS
1293 /* If the new position is before an invisible character,
1294 move forward over all such. */
1295 while (! NULL_INTERVAL_P (to)
1296 && ! INTERVAL_VISIBLE_P (to)
1297 && ! DISPLAY_INVISIBLE_GLYPH (to))
a50699fd 1298 {
5cae0ec6
RS
1299 toprev = to;
1300 to = next_interval (to);
0df8950e
RS
1301 if (NULL_INTERVAL_P (to))
1302 position = BUF_ZV (buffer);
1303 else
1304 position = to->position;
a50699fd 1305 }
323a7ad4
RS
1306
1307 buffer->text.pt = position;
a50699fd 1308
d7e3e52b
JA
1309 /* We run point-left and point-entered hooks here, iff the
1310 two intervals are not equivalent. These hooks take
323a7ad4
RS
1311 (old_point, new_point) as arguments. */
1312 if (! intervals_equal (from, to)
1313 || ! intervals_equal (fromprev, toprev))
9c79dd1b 1314 {
323a7ad4
RS
1315 Lisp_Object leave_after, leave_before, enter_after, enter_before;
1316
1317 if (fromprev)
1318 leave_after = textget (fromprev->plist, Qpoint_left);
1319 else
1320 leave_after = Qnil;
1321 if (from)
1322 leave_before = textget (from->plist, Qpoint_left);
1323 else
1324 leave_before = Qnil;
1325
1326 if (toprev)
1327 enter_after = textget (toprev->plist, Qpoint_entered);
1328 else
1329 enter_after = Qnil;
1330 if (to)
1331 enter_before = textget (to->plist, Qpoint_entered);
1332 else
1333 enter_before = Qnil;
9c79dd1b 1334
323a7ad4
RS
1335 if (! EQ (leave_before, enter_before) && !NILP (leave_before))
1336 call2 (leave_before, old_position, position);
1337 if (! EQ (leave_after, enter_after) && !NILP (leave_after))
1338 call2 (leave_after, old_position, position);
9c79dd1b 1339
323a7ad4
RS
1340 if (! EQ (enter_before, leave_before) && !NILP (enter_before))
1341 call2 (enter_before, old_position, position);
1342 if (! EQ (enter_after, leave_after) && !NILP (enter_after))
1343 call2 (enter_after, old_position, position);
9c79dd1b 1344 }
a50699fd
JA
1345}
1346
9c79dd1b 1347/* Set point temporarily, without checking any text properties. */
a50699fd 1348
9c79dd1b
JA
1349INLINE void
1350temp_set_point (position, buffer)
1351 int position;
1352 struct buffer *buffer;
1353{
1354 buffer->text.pt = position;
1355}
294efdbe 1356\f
5cae0ec6
RS
1357/* Return the proper local map for position POSITION in BUFFER.
1358 Use the map specified by the local-map property, if any.
1359 Otherwise, use BUFFER's local map. */
1360
1361Lisp_Object
1362get_local_map (position, buffer)
1363 register int position;
1364 register struct buffer *buffer;
1365{
1366 register INTERVAL interval;
1367 Lisp_Object prop, tem;
1368
1369 if (NULL_INTERVAL_P (buffer->intervals))
1370 return current_buffer->keymap;
1371
1372 /* Perhaps we should just change `position' to the limit. */
1373 if (position > BUF_Z (buffer) || position < BUF_BEG (buffer))
1374 abort ();
1375
5cae0ec6
RS
1376 interval = find_interval (buffer->intervals, position);
1377 prop = textget (interval->plist, Qlocal_map);
1378 if (NILP (prop))
1379 return current_buffer->keymap;
1380
1381 /* Use the local map only if it is valid. */
1382 tem = Fkeymapp (prop);
1383 if (!NILP (tem))
1384 return prop;
1385
1386 return current_buffer->keymap;
1387}
1388\f
294efdbe
RS
1389/* Call the modification hook functions in LIST, each with START and END. */
1390
1391static void
1392call_mod_hooks (list, start, end)
1393 Lisp_Object list, start, end;
1394{
1395 struct gcpro gcpro1;
1396 GCPRO1 (list);
1397 while (!NILP (list))
1398 {
1399 call2 (Fcar (list), start, end);
1400 list = Fcdr (list);
1401 }
1402 UNGCPRO;
1403}
9c79dd1b
JA
1404
1405/* Check for read-only intervals and signal an error if we find one.
1406 Then check for any modification hooks in the range START up to
1407 (but not including) TO. Create a list of all these hooks in
1408 lexicographic order, eliminating consecutive extra copies of the
1409 same hook. Then call those hooks in order, with START and END - 1
1410 as arguments. */
a50699fd
JA
1411
1412void
1413verify_interval_modification (buf, start, end)
1414 struct buffer *buf;
1415 int start, end;
1416{
1417 register INTERVAL intervals = buf->intervals;
294efdbe
RS
1418 register INTERVAL i, prev;
1419 Lisp_Object hooks;
1420 register Lisp_Object prev_mod_hooks;
1421 Lisp_Object mod_hooks;
9c79dd1b 1422 struct gcpro gcpro1;
a50699fd 1423
294efdbe
RS
1424 hooks = Qnil;
1425 prev_mod_hooks = Qnil;
1426 mod_hooks = Qnil;
1427
a50699fd
JA
1428 if (NULL_INTERVAL_P (intervals))
1429 return;
1430
1431 if (start > end)
1432 {
1433 int temp = start;
1434 start = end;
1435 end = temp;
1436 }
1437
294efdbe
RS
1438 /* For an insert operation, check the two chars around the position. */
1439 if (start == end)
a50699fd 1440 {
294efdbe
RS
1441 INTERVAL prev;
1442 Lisp_Object before, after;
a50699fd 1443
294efdbe
RS
1444 /* Set I to the interval containing the char after START,
1445 and PREV to the interval containing the char before START.
1446 Either one may be null. They may be equal. */
24e3d3bf 1447 i = find_interval (intervals, start);
294efdbe
RS
1448
1449 if (start == BUF_BEGV (buf))
1450 prev = 0;
1451 if (i->position == start)
1452 prev = previous_interval (i);
1453 else if (i->position < start)
1454 prev = i;
1455 if (start == BUF_ZV (buf))
1456 i = 0;
1457
1458 if (NULL_INTERVAL_P (prev))
1459 {
7c92db56 1460 if (! INTERVAL_WRITABLE_P (i))
294efdbe
RS
1461 error ("Attempt to insert within read-only text");
1462 }
1463 else if (NULL_INTERVAL_P (i))
1464 {
7c92db56 1465 if (! INTERVAL_WRITABLE_P (prev))
294efdbe
RS
1466 error ("Attempt to insert within read-only text");
1467 }
1468 else
1469 {
5cae0ec6
RS
1470 before = textget (prev->plist, Qread_only);
1471 after = textget (i->plist, Qread_only);
7c92db56
RS
1472 if (! NILP (before) && EQ (before, after)
1473 /* This checks Vinhibit_read_only properly
1474 for the common value of the read-only property. */
1475 && ! INTERVAL_WRITABLE_P (i))
294efdbe
RS
1476 error ("Attempt to insert within read-only text");
1477 }
1478
c3649419 1479 /* Run both insert hooks (just once if they're the same). */
294efdbe 1480 if (!NULL_INTERVAL_P (prev))
f1ca9012 1481 prev_mod_hooks = textget (prev->plist, Qinsert_behind_hooks);
294efdbe 1482 if (!NULL_INTERVAL_P (i))
f1ca9012 1483 mod_hooks = textget (i->plist, Qinsert_in_front_hooks);
294efdbe
RS
1484 GCPRO1 (mod_hooks);
1485 if (! NILP (prev_mod_hooks))
1486 call_mod_hooks (prev_mod_hooks, make_number (start),
1487 make_number (end));
1488 UNGCPRO;
1489 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1490 call_mod_hooks (mod_hooks, make_number (start), make_number (end));
a50699fd
JA
1491 }
1492 else
a50699fd 1493 {
294efdbe
RS
1494 /* Loop over intervals on or next to START...END,
1495 collecting their hooks. */
9c79dd1b 1496
294efdbe
RS
1497 i = find_interval (intervals, start);
1498 do
9c79dd1b 1499 {
294efdbe
RS
1500 if (! INTERVAL_WRITABLE_P (i))
1501 error ("Attempt to modify read-only text");
9c79dd1b 1502
294efdbe
RS
1503 mod_hooks = textget (i->plist, Qmodification_hooks);
1504 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1505 {
1506 hooks = Fcons (mod_hooks, hooks);
1507 prev_mod_hooks = mod_hooks;
1508 }
a50699fd 1509
294efdbe
RS
1510 i = next_interval (i);
1511 }
1512 /* Keep going thru the interval containing the char before END. */
1513 while (! NULL_INTERVAL_P (i) && i->position < end);
1514
1515 GCPRO1 (hooks);
1516 hooks = Fnreverse (hooks);
1517 while (! EQ (hooks, Qnil))
1518 {
1519 call_mod_hooks (Fcar (hooks), make_number (start),
1520 make_number (end));
1521 hooks = Fcdr (hooks);
1522 }
1523 UNGCPRO;
9c79dd1b 1524 }
a50699fd
JA
1525}
1526
1527/* Balance an interval node if the amount of text in its left and right
1528 subtrees differs by more than the percentage specified by
1529 `interval-balance-threshold'. */
1530
1531static INTERVAL
1532balance_an_interval (i)
1533 INTERVAL i;
1534{
1535 register int total_children_size = (LEFT_TOTAL_LENGTH (i)
1536 + RIGHT_TOTAL_LENGTH (i));
1537 register int threshold = (XFASTINT (interval_balance_threshold)
1538 * (total_children_size / 100));
1539
95e3e1ef
RS
1540 /* Balance within each side. */
1541 balance_intervals (i->left);
1542 balance_intervals (i->right);
a50699fd
JA
1543
1544 if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
1545 && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
95e3e1ef
RS
1546 {
1547 i = rotate_right (i);
1548 /* If that made it unbalanced the other way, take it back. */
1549 if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
1550 && (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
1551 return rotate_left (i);
1552 return i;
1553 }
a50699fd 1554
95e3e1ef
RS
1555 if (RIGHT_TOTAL_LENGTH (i) > LEFT_TOTAL_LENGTH (i)
1556 && (RIGHT_TOTAL_LENGTH (i) - LEFT_TOTAL_LENGTH (i)) > threshold)
1557 {
1558 i = rotate_left (i);
1559 if (LEFT_TOTAL_LENGTH (i) > RIGHT_TOTAL_LENGTH (i)
1560 && (LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i)) > threshold)
1561 return rotate_right (i);
1562 return i;
1563 }
a50699fd
JA
1564
1565 return i;
1566}
1567
1568/* Balance the interval tree TREE. Balancing is by weight
1569 (the amount of text). */
1570
1571INTERVAL
1572balance_intervals (tree)
1573 register INTERVAL tree;
1574{
1575 register INTERVAL new_tree;
1576
1577 if (NULL_INTERVAL_P (tree))
1578 return NULL_INTERVAL;
1579
1580 new_tree = tree;
1581 do
1582 {
1583 tree = new_tree;
1584 new_tree = balance_an_interval (new_tree);
1585 }
1586 while (new_tree != tree);
1587
1588 return new_tree;
1589}
1590
9c79dd1b 1591/* Produce an interval tree reflecting the intervals in
a50699fd
JA
1592 TREE from START to START + LENGTH. */
1593
7b1d5b85 1594INTERVAL
a50699fd
JA
1595copy_intervals (tree, start, length)
1596 INTERVAL tree;
1597 int start, length;
1598{
1599 register INTERVAL i, new, t;
95e3e1ef 1600 register int got, prevlen;
a50699fd
JA
1601
1602 if (NULL_INTERVAL_P (tree) || length <= 0)
1603 return NULL_INTERVAL;
1604
1605 i = find_interval (tree, start);
1606 if (NULL_INTERVAL_P (i) || LENGTH (i) == 0)
1607 abort ();
1608
1609 /* If there is only one interval and it's the default, return nil. */
1610 if ((start - i->position + 1 + length) < LENGTH (i)
1611 && DEFAULT_INTERVAL_P (i))
1612 return NULL_INTERVAL;
1613
1614 new = make_interval ();
1615 new->position = 1;
1616 got = (LENGTH (i) - (start - i->position));
9c79dd1b 1617 new->total_length = length;
a50699fd
JA
1618 copy_properties (i, new);
1619
1620 t = new;
95e3e1ef 1621 prevlen = got;
a50699fd
JA
1622 while (got < length)
1623 {
1624 i = next_interval (i);
2bc7a79b 1625 t = split_interval_right (t, prevlen);
a50699fd 1626 copy_properties (i, t);
95e3e1ef
RS
1627 prevlen = LENGTH (i);
1628 got += prevlen;
a50699fd
JA
1629 }
1630
a50699fd
JA
1631 return balance_intervals (new);
1632}
1633
a50699fd
JA
1634/* Give STRING the properties of BUFFER from POSITION to LENGTH. */
1635
d7e3e52b 1636INLINE void
a50699fd
JA
1637copy_intervals_to_string (string, buffer, position, length)
1638 Lisp_Object string, buffer;
1639 int position, length;
1640{
1641 INTERVAL interval_copy = copy_intervals (XBUFFER (buffer)->intervals,
1642 position, length);
1643 if (NULL_INTERVAL_P (interval_copy))
1644 return;
1645
1646 interval_copy->parent = (INTERVAL) string;
1647 XSTRING (string)->intervals = interval_copy;
1648}
d2f7a802
JA
1649
1650#endif /* USE_TEXT_PROPERTIES */