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