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