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