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