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