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