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