* lisp/mouse.el (popup-menu-normalize-position): New function.
[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
65e8ee52
DA
1562 If the text goes into the middle of an interval, then new intervals
1563 are created in the middle, and new text has the union of its properties
1564 and those of the text into which it was inserted.
a50699fd
JA
1565
1566 If the text goes between two intervals, then if neither interval
90ba40fc
JA
1567 had its appropriate sticky property set (front_sticky, rear_sticky),
1568 the new text has only its properties. If one of the sticky properties
a50699fd 1569 is set, then the new text "sticks" to that region and its properties
eb8c3be9 1570 depend on merging as above. If both the preceding and succeeding
90ba40fc
JA
1571 intervals to the new text are "sticky", then the new text retains
1572 only its properties, as if neither sticky property were set. Perhaps
a50699fd 1573 we should consider merging all three sets of properties onto the new
7ce503fd 1574 text... */
a50699fd
JA
1575
1576void
d311d28c
PE
1577graft_intervals_into_buffer (INTERVAL source, ptrdiff_t position,
1578 ptrdiff_t length, struct buffer *buffer,
e79123aa 1579 int inherit)
a50699fd 1580{
dc6c6455 1581 register INTERVAL under, over, this;
e5d967c9 1582 register INTERVAL tree;
d311d28c 1583 ptrdiff_t over_used;
a50699fd 1584
8707c1e5 1585 tree = buffer_get_intervals (buffer);
e5d967c9 1586
34e096ed
RS
1587 /* If the new text has no properties, then with inheritance it
1588 becomes part of whatever interval it was inserted into.
1589 To prevent inheritance, we must clear out the properties
1590 of the newly inserted text. */
77c7bcb1 1591 if (!source)
0b79989f
RS
1592 {
1593 Lisp_Object buf;
77c7bcb1 1594 if (!inherit && tree && length > 0)
0b79989f 1595 {
55cfc731 1596 XSETBUFFER (buf, buffer);
34e096ed
RS
1597 set_text_properties_1 (make_number (position),
1598 make_number (position + length),
1599 Qnil, buf, 0);
0b79989f 1600 }
8707c1e5
DA
1601 /* Shouldn't be necessary. --Stef */
1602 buffer_balance_intervals (buffer);
0b79989f
RS
1603 return;
1604 }
a50699fd 1605
b50a28de
SM
1606 eassert (length == TOTAL_LENGTH (source));
1607
1608 if ((BUF_Z (buffer) - BUF_BEG (buffer)) == length)
8707c1e5
DA
1609 {
1610 /* The inserted text constitutes the whole buffer, so
7ce503fd 1611 simply copy over the interval structure. */
8707c1e5
DA
1612 Lisp_Object buf;
1613
1614 XSETBUFFER (buf, buffer);
1615 buffer_set_intervals (buffer, reproduce_tree_obj (source, buf));
1616 buffer_get_intervals (buffer)->position = BUF_BEG (buffer);
1617 eassert (buffer_get_intervals (buffer)->up_obj == 1);
1618 return;
1619 }
77c7bcb1 1620 else if (!tree)
8707c1e5
DA
1621 {
1622 /* Create an interval tree in which to place a copy
7ce503fd 1623 of the intervals of the inserted string. */
249a6da9 1624 Lisp_Object buf;
8707c1e5 1625
55cfc731 1626 XSETBUFFER (buf, buffer);
323a7ad4 1627 tree = create_root_interval (buf);
8707c1e5 1628 }
cce7fefc
DA
1629 /* Paranoia -- the text has already been added, so
1630 this buffer should be of non-zero length. */
1631 eassert (TOTAL_LENGTH (tree) > 0);
a50699fd
JA
1632
1633 this = under = find_interval (tree, position);
77c7bcb1 1634 eassert (under);
944d4e4b 1635 over = find_interval (source, interval_start_pos (source));
a50699fd 1636
323a7ad4
RS
1637 /* Here for insertion in the middle of an interval.
1638 Split off an equivalent interval to the right,
1639 then don't bother with it any more. */
a50699fd 1640
323a7ad4 1641 if (position > under->position)
a50699fd
JA
1642 {
1643 INTERVAL end_unchanged
2bc7a79b 1644 = split_interval_left (this, position - under->position);
a50699fd 1645 copy_properties (under, end_unchanged);
323a7ad4 1646 under->position = position;
a50699fd 1647 }
323a7ad4
RS
1648 else
1649 {
f56b42ac
KH
1650 /* This call may have some effect because previous_interval may
1651 update `position' fields of intervals. Thus, don't ignore it
1652 for the moment. Someone please tell me the truth (K.Handa). */
dc6c6455
PE
1653 INTERVAL prev = previous_interval (under);
1654 (void) prev;
f56b42ac
KH
1655#if 0
1656 /* But, this code surely has no effect. And, anyway,
1657 END_NONSTICKY_P is unreliable now. */
7ce503fd 1658 if (prev && !END_NONSTICKY_P (prev))
323a7ad4 1659 prev = 0;
f56b42ac 1660#endif /* 0 */
323a7ad4
RS
1661 }
1662
1663 /* Insertion is now at beginning of UNDER. */
a50699fd 1664
323a7ad4 1665 /* The inserted text "sticks" to the interval `under',
7ce503fd
RS
1666 which means it gets those properties.
1667 The properties of under are the result of
8e6208c5 1668 adjust_intervals_for_insertion, so stickiness has
7ce503fd 1669 already been taken care of. */
7d0393cf 1670
6b61353c
KH
1671 /* OVER is the interval we are copying from next.
1672 OVER_USED says how many characters' worth of OVER
1673 have already been copied into target intervals.
1674 UNDER is the next interval in the target. */
1675 over_used = 0;
77c7bcb1 1676 while (over)
a50699fd 1677 {
6b61353c
KH
1678 /* If UNDER is longer than OVER, split it. */
1679 if (LENGTH (over) - over_used < LENGTH (under))
7ce503fd 1680 {
6b61353c 1681 this = split_interval_left (under, LENGTH (over) - over_used);
7ce503fd
RS
1682 copy_properties (under, this);
1683 }
323a7ad4
RS
1684 else
1685 this = under;
6b61353c
KH
1686
1687 /* THIS is now the interval to copy or merge into.
1688 OVER covers all of it. */
7ea69158 1689 if (inherit)
7ce503fd
RS
1690 merge_properties (over, this);
1691 else
1692 copy_properties (over, this);
6b61353c
KH
1693
1694 /* If THIS and OVER end at the same place,
1695 advance OVER to a new source interval. */
1696 if (LENGTH (this) == LENGTH (over) - over_used)
1697 {
1698 over = next_interval (over);
1699 over_used = 0;
1700 }
1701 else
1702 /* Otherwise just record that more of OVER has been used. */
1703 over_used += LENGTH (this);
1704
1705 /* Always advance to a new target interval. */
1706 under = next_interval (this);
a50699fd
JA
1707 }
1708
8707c1e5 1709 buffer_balance_intervals (buffer);
a50699fd
JA
1710}
1711
5cae0ec6
RS
1712/* Get the value of property PROP from PLIST,
1713 which is the plist of an interval.
7d0393cf 1714 We check for direct properties, for categories with property PROP,
06d92327 1715 and for PROP appearing on the default-text-properties list. */
5cae0ec6
RS
1716
1717Lisp_Object
971de7fb 1718textget (Lisp_Object plist, register Lisp_Object prop)
323a7ad4 1719{
91b53904 1720 return lookup_char_property (plist, prop, 1);
948fe32d
CW
1721}
1722
1723Lisp_Object
971de7fb 1724lookup_char_property (Lisp_Object plist, register Lisp_Object prop, int textprop)
948fe32d
CW
1725{
1726 register Lisp_Object tail, fallback = Qnil;
323a7ad4 1727
91b53904 1728 for (tail = plist; CONSP (tail); tail = Fcdr (XCDR (tail)))
323a7ad4
RS
1729 {
1730 register Lisp_Object tem;
91b53904 1731 tem = XCAR (tail);
323a7ad4 1732 if (EQ (prop, tem))
91b53904 1733 return Fcar (XCDR (tail));
5cae0ec6 1734 if (EQ (tem, Qcategory))
5dd6606e 1735 {
91b53904 1736 tem = Fcar (XCDR (tail));
5dd6606e
RS
1737 if (SYMBOLP (tem))
1738 fallback = Fget (tem, prop);
1739 }
323a7ad4 1740 }
5cae0ec6 1741
70743ff1
BG
1742 if (! NILP (fallback))
1743 return fallback;
948fe32d
CW
1744 /* Check for alternative properties */
1745 tail = Fassq (prop, Vchar_property_alias_alist);
931285e2
LT
1746 if (! NILP (tail))
1747 {
1748 tail = XCDR (tail);
1749 for (; NILP (fallback) && CONSP (tail); tail = XCDR (tail))
1750 fallback = Fplist_get (plist, XCAR (tail));
1751 }
1752
948fe32d
CW
1753 if (textprop && NILP (fallback) && CONSP (Vdefault_text_properties))
1754 fallback = Fplist_get (Vdefault_text_properties, prop);
1755 return fallback;
323a7ad4 1756}
7ce503fd 1757
294efdbe 1758\f
ef1900f3
RS
1759/* Set point in BUFFER "temporarily" to CHARPOS, which corresponds to
1760 byte position BYTEPOS. */
1761
09db192c 1762void
6ba7f443 1763temp_set_point_both (struct buffer *buffer,
d311d28c 1764 ptrdiff_t charpos, ptrdiff_t bytepos)
ef1900f3
RS
1765{
1766 /* In a single-byte buffer, the two positions must be equal. */
cce7fefc
DA
1767 if (BUF_ZV (buffer) == BUF_ZV_BYTE (buffer))
1768 eassert (charpos == bytepos);
ef1900f3 1769
cce7fefc
DA
1770 eassert (charpos <= bytepos);
1771 eassert (charpos <= BUF_ZV (buffer) || BUF_BEGV (buffer) <= charpos);
ef1900f3 1772
cffc6f3b 1773 SET_BUF_PT_BOTH (buffer, charpos, bytepos);
ef1900f3
RS
1774}
1775
f8ab8c1f
EZ
1776/* Set point "temporarily", without checking any text properties. */
1777
09db192c 1778void
d311d28c 1779temp_set_point (struct buffer *buffer, ptrdiff_t charpos)
f8ab8c1f
EZ
1780{
1781 temp_set_point_both (buffer, charpos,
1782 buf_charpos_to_bytepos (buffer, charpos));
1783}
1784
7d0393cf 1785/* Set point in BUFFER to CHARPOS. If the target position is
f65013b0 1786 before an intangible character, move to an ok place. */
a50699fd
JA
1787
1788void
d311d28c 1789set_point (ptrdiff_t charpos)
ef1900f3 1790{
6ba7f443 1791 set_point_both (charpos, buf_charpos_to_bytepos (current_buffer, charpos));
ef1900f3
RS
1792}
1793
f0dcf801
MB
1794/* If there's an invisible character at position POS + TEST_OFFS in the
1795 current buffer, and the invisible property has a `stickiness' such that
1796 inserting a character at position POS would inherit the property it,
1797 return POS + ADJ, otherwise return POS. If TEST_INTANG is non-zero,
53964682 1798 then intangibility is required as well as invisibility.
f0dcf801
MB
1799
1800 TEST_OFFS should be either 0 or -1, and ADJ should be either 1 or -1.
1801
1802 Note that `stickiness' is determined by overlay marker insertion types,
7d0393cf 1803 if the invisible property comes from an overlay. */
f0dcf801 1804
d311d28c
PE
1805static ptrdiff_t
1806adjust_for_invis_intang (ptrdiff_t pos, ptrdiff_t test_offs, ptrdiff_t adj,
e79123aa 1807 int test_intang)
f0dcf801
MB
1808{
1809 Lisp_Object invis_propval, invis_overlay;
1810 Lisp_Object test_pos;
1811
1812 if ((adj < 0 && pos + adj < BEGV) || (adj > 0 && pos + adj > ZV))
1813 /* POS + ADJ would be beyond the buffer bounds, so do no adjustment. */
1814 return pos;
1815
1816 test_pos = make_number (pos + test_offs);
1817
1818 invis_propval
1819 = get_char_property_and_overlay (test_pos, Qinvisible, Qnil,
1820 &invis_overlay);
1821
1822 if ((!test_intang
1823 || ! NILP (Fget_char_property (test_pos, Qintangible, Qnil)))
1824 && TEXT_PROP_MEANS_INVISIBLE (invis_propval)
1825 /* This next test is true if the invisible property has a stickiness
1826 such that an insertion at POS would inherit it. */
1827 && (NILP (invis_overlay)
1828 /* Invisible property is from a text-property. */
4867a283 1829 ? (text_property_stickiness (Qinvisible, make_number (pos), Qnil)
f0dcf801
MB
1830 == (test_offs == 0 ? 1 : -1))
1831 /* Invisible property is from an overlay. */
1832 : (test_offs == 0
1833 ? XMARKER (OVERLAY_START (invis_overlay))->insertion_type == 0
1834 : XMARKER (OVERLAY_END (invis_overlay))->insertion_type == 1)))
1835 pos += adj;
1836
1837 return pos;
1838}
1839
ef1900f3 1840/* Set point in BUFFER to CHARPOS, which corresponds to byte
7d0393cf 1841 position BYTEPOS. If the target position is
ef1900f3
RS
1842 before an intangible character, move to an ok place. */
1843
1844void
d311d28c 1845set_point_both (ptrdiff_t charpos, ptrdiff_t bytepos)
a50699fd 1846{
e39adcda 1847 register INTERVAL to, from, toprev, fromprev;
d311d28c
PE
1848 ptrdiff_t buffer_point;
1849 ptrdiff_t old_position = PT;
594a1605
CY
1850 /* This ensures that we move forward past intangible text when the
1851 initial position is the same as the destination, in the rare
1852 instances where this is important, e.g. in line-move-finish
1853 (simple.el). */
ef1900f3 1854 int backwards = (charpos < old_position ? 1 : 0);
580fae94 1855 int have_overlays;
d311d28c 1856 ptrdiff_t original_position;
a50699fd 1857
4b4deea2 1858 BVAR (current_buffer, point_before_scroll) = Qnil;
b6a0ebc3 1859
6ba7f443 1860 if (charpos == PT)
a50699fd
JA
1861 return;
1862
ef1900f3 1863 /* In a single-byte buffer, the two positions must be equal. */
6ba7f443 1864 eassert (ZV != ZV_BYTE || charpos == bytepos);
ef1900f3 1865
62056764
JB
1866 /* Check this now, before checking if the buffer has any intervals.
1867 That way, we can catch conditions which break this sanity check
1868 whether or not there are intervals in the buffer. */
6ba7f443 1869 eassert (charpos <= ZV && charpos >= BEGV);
62056764 1870
4cb3e6b3 1871 have_overlays = buffer_has_overlays ();
580fae94
RS
1872
1873 /* If we have no text properties and overlays,
1874 then we can do it quickly. */
8707c1e5 1875 if (!buffer_get_intervals (current_buffer) && ! have_overlays)
a50699fd 1876 {
6ba7f443 1877 temp_set_point_both (current_buffer, charpos, bytepos);
a50699fd
JA
1878 return;
1879 }
1880
ef1900f3
RS
1881 /* Set TO to the interval containing the char after CHARPOS,
1882 and TOPREV to the interval containing the char before CHARPOS.
323a7ad4 1883 Either one may be null. They may be equal. */
8707c1e5 1884 to = find_interval (buffer_get_intervals (current_buffer), charpos);
6ba7f443 1885 if (charpos == BEGV)
294efdbe 1886 toprev = 0;
ef1900f3 1887 else if (to && to->position == charpos)
323a7ad4 1888 toprev = previous_interval (to);
323a7ad4
RS
1889 else
1890 toprev = to;
1891
6ba7f443 1892 buffer_point = (PT == ZV ? ZV - 1 : PT);
9c79dd1b 1893
323a7ad4
RS
1894 /* Set FROM to the interval containing the char after PT,
1895 and FROMPREV to the interval containing the char before PT.
1896 Either one may be null. They may be equal. */
7ce503fd 1897 /* We could cache this and save time. */
8707c1e5 1898 from = find_interval (buffer_get_intervals (current_buffer), buffer_point);
6ba7f443 1899 if (buffer_point == BEGV)
294efdbe 1900 fromprev = 0;
6ba7f443 1901 else if (from && from->position == PT)
323a7ad4 1902 fromprev = previous_interval (from);
6ba7f443 1903 else if (buffer_point != PT)
323a7ad4
RS
1904 fromprev = from, from = 0;
1905 else
1906 fromprev = from;
a50699fd 1907
7ce503fd 1908 /* Moving within an interval. */
580fae94
RS
1909 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to)
1910 && ! have_overlays)
a50699fd 1911 {
6ba7f443 1912 temp_set_point_both (current_buffer, charpos, bytepos);
a50699fd
JA
1913 return;
1914 }
1915
ef1900f3 1916 original_position = charpos;
580fae94 1917
5eabb4e7
RS
1918 /* If the new position is between two intangible characters
1919 with the same intangible property value,
1920 move forward or backward until a change in that property. */
580fae94 1921 if (NILP (Vinhibit_point_motion_hooks)
77c7bcb1 1922 && ((to && toprev)
b827a9e3
RS
1923 || have_overlays)
1924 /* Intangibility never stops us from positioning at the beginning
1925 or end of the buffer, so don't bother checking in that case. */
ef1900f3 1926 && charpos != BEGV && charpos != ZV)
a50699fd 1927 {
580fae94 1928 Lisp_Object pos;
f0dcf801 1929 Lisp_Object intangible_propval;
580fae94 1930
d5219de5
RS
1931 if (backwards)
1932 {
0270b877 1933 /* If the preceding character is both intangible and invisible,
f0dcf801
MB
1934 and the invisible property is `rear-sticky', perturb it so
1935 that the search starts one character earlier -- this ensures
1936 that point can never move to the end of an invisible/
1937 intangible/rear-sticky region. */
1938 charpos = adjust_for_invis_intang (charpos, -1, -1, 1);
1d14d232 1939
f0dcf801 1940 XSETINT (pos, charpos);
5eabb4e7
RS
1941
1942 /* If following char is intangible,
1943 skip back over all chars with matching intangible property. */
1d14d232
RS
1944
1945 intangible_propval = Fget_char_property (pos, Qintangible, Qnil);
1946
5eabb4e7 1947 if (! NILP (intangible_propval))
1d14d232 1948 {
6ba7f443 1949 while (XINT (pos) > BEGV
1d14d232
RS
1950 && EQ (Fget_char_property (make_number (XINT (pos) - 1),
1951 Qintangible, Qnil),
1952 intangible_propval))
1953 pos = Fprevious_char_property_change (pos, Qnil);
f0dcf801
MB
1954
1955 /* Set CHARPOS from POS, and if the final intangible character
1956 that we skipped over is also invisible, and the invisible
1957 property is `front-sticky', perturb it to be one character
1958 earlier -- this ensures that point can never move to the
1959 beginning of an invisible/intangible/front-sticky region. */
1960 charpos = adjust_for_invis_intang (XINT (pos), 0, -1, 0);
1d14d232 1961 }
d5219de5 1962 }
0df8950e 1963 else
d5219de5 1964 {
f0dcf801
MB
1965 /* If the following character is both intangible and invisible,
1966 and the invisible property is `front-sticky', perturb it so
1967 that the search starts one character later -- this ensures
1968 that point can never move to the beginning of an
1969 invisible/intangible/front-sticky region. */
1970 charpos = adjust_for_invis_intang (charpos, 0, 1, 1);
1971
1972 XSETINT (pos, charpos);
1973
1d14d232
RS
1974 /* If preceding char is intangible,
1975 skip forward over all chars with matching intangible property. */
1976
ef1900f3 1977 intangible_propval = Fget_char_property (make_number (charpos - 1),
580fae94 1978 Qintangible, Qnil);
5eabb4e7 1979
5eabb4e7 1980 if (! NILP (intangible_propval))
1d14d232 1981 {
6ba7f443 1982 while (XINT (pos) < ZV
1d14d232
RS
1983 && EQ (Fget_char_property (pos, Qintangible, Qnil),
1984 intangible_propval))
1985 pos = Fnext_char_property_change (pos, Qnil);
580fae94 1986
f0dcf801
MB
1987 /* Set CHARPOS from POS, and if the final intangible character
1988 that we skipped over is also invisible, and the invisible
1989 property is `rear-sticky', perturb it to be one character
1990 later -- this ensures that point can never move to the
1991 end of an invisible/intangible/rear-sticky region. */
1992 charpos = adjust_for_invis_intang (XINT (pos), -1, 1, 0);
1d14d232 1993 }
d5219de5 1994 }
580fae94 1995
6ba7f443 1996 bytepos = buf_charpos_to_bytepos (current_buffer, charpos);
580fae94
RS
1997 }
1998
ef1900f3 1999 if (charpos != original_position)
580fae94 2000 {
ef1900f3
RS
2001 /* Set TO to the interval containing the char after CHARPOS,
2002 and TOPREV to the interval containing the char before CHARPOS.
580fae94 2003 Either one may be null. They may be equal. */
8707c1e5 2004 to = find_interval (buffer_get_intervals (current_buffer), charpos);
6ba7f443 2005 if (charpos == BEGV)
580fae94 2006 toprev = 0;
ef1900f3 2007 else if (to && to->position == charpos)
580fae94
RS
2008 toprev = previous_interval (to);
2009 else
2010 toprev = to;
a50699fd 2011 }
323a7ad4 2012
5eabb4e7
RS
2013 /* Here TO is the interval after the stopping point
2014 and TOPREV is the interval before the stopping point.
2015 One or the other may be null. */
2016
6ba7f443 2017 temp_set_point_both (current_buffer, charpos, bytepos);
a50699fd 2018
e0f24100 2019 /* We run point-left and point-entered hooks here, if the
d7e3e52b 2020 two intervals are not equivalent. These hooks take
323a7ad4 2021 (old_point, new_point) as arguments. */
ddd931ff
RS
2022 if (NILP (Vinhibit_point_motion_hooks)
2023 && (! intervals_equal (from, to)
2024 || ! intervals_equal (fromprev, toprev)))
9c79dd1b 2025 {
323a7ad4
RS
2026 Lisp_Object leave_after, leave_before, enter_after, enter_before;
2027
2028 if (fromprev)
4e8f005c 2029 leave_before = textget (fromprev->plist, Qpoint_left);
323a7ad4 2030 else
4e8f005c
CY
2031 leave_before = Qnil;
2032
323a7ad4 2033 if (from)
4e8f005c 2034 leave_after = textget (from->plist, Qpoint_left);
323a7ad4 2035 else
4e8f005c 2036 leave_after = Qnil;
323a7ad4
RS
2037
2038 if (toprev)
4e8f005c 2039 enter_before = textget (toprev->plist, Qpoint_entered);
323a7ad4 2040 else
4e8f005c
CY
2041 enter_before = Qnil;
2042
323a7ad4 2043 if (to)
4e8f005c 2044 enter_after = textget (to->plist, Qpoint_entered);
323a7ad4 2045 else
4e8f005c 2046 enter_after = Qnil;
9c79dd1b 2047
323a7ad4 2048 if (! EQ (leave_before, enter_before) && !NILP (leave_before))
4e8f005c
CY
2049 call2 (leave_before, make_number (old_position),
2050 make_number (charpos));
323a7ad4 2051 if (! EQ (leave_after, enter_after) && !NILP (leave_after))
4e8f005c
CY
2052 call2 (leave_after, make_number (old_position),
2053 make_number (charpos));
9c79dd1b 2054
323a7ad4 2055 if (! EQ (enter_before, leave_before) && !NILP (enter_before))
4e8f005c
CY
2056 call2 (enter_before, make_number (old_position),
2057 make_number (charpos));
323a7ad4 2058 if (! EQ (enter_after, leave_after) && !NILP (enter_after))
4e8f005c
CY
2059 call2 (enter_after, make_number (old_position),
2060 make_number (charpos));
9c79dd1b 2061 }
a50699fd 2062}
294efdbe 2063\f
a7fa233f
RS
2064/* Move point to POSITION, unless POSITION is inside an intangible
2065 segment that reaches all the way to point. */
2066
2067void
d311d28c 2068move_if_not_intangible (ptrdiff_t position)
a7fa233f
RS
2069{
2070 Lisp_Object pos;
2071 Lisp_Object intangible_propval;
2072
2073 XSETINT (pos, position);
2074
2075 if (! NILP (Vinhibit_point_motion_hooks))
2076 /* If intangible is inhibited, always move point to POSITION. */
2077 ;
2e34157c 2078 else if (PT < position && XINT (pos) < ZV)
a7fa233f
RS
2079 {
2080 /* We want to move forward, so check the text before POSITION. */
2081
2082 intangible_propval = Fget_char_property (pos,
2083 Qintangible, Qnil);
2084
2085 /* If following char is intangible,
2086 skip back over all chars with matching intangible property. */
2087 if (! NILP (intangible_propval))
2088 while (XINT (pos) > BEGV
2089 && EQ (Fget_char_property (make_number (XINT (pos) - 1),
2090 Qintangible, Qnil),
2091 intangible_propval))
2092 pos = Fprevious_char_property_change (pos, Qnil);
2093 }
2e34157c 2094 else if (XINT (pos) > BEGV)
a7fa233f
RS
2095 {
2096 /* We want to move backward, so check the text after POSITION. */
2097
2098 intangible_propval = Fget_char_property (make_number (XINT (pos) - 1),
2099 Qintangible, Qnil);
2100
2101 /* If following char is intangible,
887f2a2d 2102 skip forward over all chars with matching intangible property. */
a7fa233f
RS
2103 if (! NILP (intangible_propval))
2104 while (XINT (pos) < ZV
2105 && EQ (Fget_char_property (pos, Qintangible, Qnil),
2106 intangible_propval))
2107 pos = Fnext_char_property_change (pos, Qnil);
2108
2109 }
97d8f112
RS
2110 else if (position < BEGV)
2111 position = BEGV;
2112 else if (position > ZV)
2113 position = ZV;
a7fa233f 2114
7d0393cf 2115 /* If the whole stretch between PT and POSITION isn't intangible,
a7fa233f
RS
2116 try moving to POSITION (which means we actually move farther
2117 if POSITION is inside of intangible text). */
2118
2119 if (XINT (pos) != PT)
2120 SET_PT (position);
2121}
2122\f
f56b42ac
KH
2123/* If text at position POS has property PROP, set *VAL to the property
2124 value, *START and *END to the beginning and end of a region that
2125 has the same property, and return 1. Otherwise return 0.
2126
2127 OBJECT is the string or buffer to look for the property in;
2128 nil means the current buffer. */
2129
2130int
d311d28c
PE
2131get_property_and_range (ptrdiff_t pos, Lisp_Object prop, Lisp_Object *val,
2132 ptrdiff_t *start, ptrdiff_t *end, Lisp_Object object)
f56b42ac
KH
2133{
2134 INTERVAL i, prev, next;
2135
2136 if (NILP (object))
8707c1e5 2137 i = find_interval (buffer_get_intervals (current_buffer), pos);
f56b42ac 2138 else if (BUFFERP (object))
8707c1e5 2139 i = find_interval (buffer_get_intervals (XBUFFER (object)), pos);
f56b42ac 2140 else if (STRINGP (object))
ad8c997f 2141 i = find_interval (string_get_intervals (object), pos);
f56b42ac
KH
2142 else
2143 abort ();
2144
77c7bcb1 2145 if (!i || (i->position + LENGTH (i) <= pos))
f56b42ac
KH
2146 return 0;
2147 *val = textget (i->plist, prop);
2148 if (NILP (*val))
2149 return 0;
2150
2151 next = i; /* remember it in advance */
2152 prev = previous_interval (i);
77c7bcb1 2153 while (prev
f56b42ac
KH
2154 && EQ (*val, textget (prev->plist, prop)))
2155 i = prev, prev = previous_interval (prev);
2156 *start = i->position;
2157
2158 next = next_interval (i);
77c7bcb1 2159 while (next && EQ (*val, textget (next->plist, prop)))
f56b42ac
KH
2160 i = next, next = next_interval (next);
2161 *end = i->position + LENGTH (i);
2162
2163 return 1;
2164}
2165\f
2b4b027f
GM
2166/* Return the proper local keymap TYPE for position POSITION in
2167 BUFFER; TYPE should be one of `keymap' or `local-map'. Use the map
2168 specified by the PROP property, if any. Otherwise, if TYPE is
1b0440ed
RS
2169 `local-map' use BUFFER's local map.
2170
2171 POSITION must be in the accessible part of BUFFER. */
5cae0ec6
RS
2172
2173Lisp_Object
d311d28c 2174get_local_map (register ptrdiff_t position, register struct buffer *buffer,
e79123aa 2175 Lisp_Object type)
5cae0ec6 2176{
f94ecad1 2177 Lisp_Object prop, lispy_position, lispy_buffer;
d311d28c 2178 ptrdiff_t old_begv, old_zv, old_begv_byte, old_zv_byte;
5cae0ec6 2179
7ce503fd 2180 /* Perhaps we should just change `position' to the limit. */
1b0440ed 2181 if (position > BUF_ZV (buffer) || position < BUF_BEGV (buffer))
5cae0ec6
RS
2182 abort ();
2183
0f7a5fda
KH
2184 /* Ignore narrowing, so that a local map continues to be valid even if
2185 the visible region contains no characters and hence no properties. */
2186 old_begv = BUF_BEGV (buffer);
2187 old_zv = BUF_ZV (buffer);
ef1900f3
RS
2188 old_begv_byte = BUF_BEGV_BYTE (buffer);
2189 old_zv_byte = BUF_ZV_BYTE (buffer);
cffc6f3b
CY
2190
2191 SET_BUF_BEGV_BOTH (buffer, BUF_BEG (buffer), BUF_BEG_BYTE (buffer));
2192 SET_BUF_ZV_BOTH (buffer, BUF_Z (buffer), BUF_Z_BYTE (buffer));
0f7a5fda 2193
0f7a5fda
KH
2194 XSETFASTINT (lispy_position, position);
2195 XSETBUFFER (lispy_buffer, buffer);
4867a283
SM
2196 /* First check if the CHAR has any property. This is because when
2197 we click with the mouse, the mouse pointer is really pointing
2198 to the CHAR after POS. */
2b4b027f 2199 prop = Fget_char_property (lispy_position, type, lispy_buffer);
4867a283
SM
2200 /* If not, look at the POS's properties. This is necessary because when
2201 editing a field with a `local-map' property, we want insertion at the end
2202 to obey the `local-map' property. */
2203 if (NILP (prop))
2204 prop = get_pos_property (lispy_position, type, lispy_buffer);
0f7a5fda 2205
cffc6f3b
CY
2206 SET_BUF_BEGV_BOTH (buffer, old_begv, old_begv_byte);
2207 SET_BUF_ZV_BOTH (buffer, old_zv, old_zv_byte);
5cae0ec6
RS
2208
2209 /* Use the local map only if it is valid. */
02067692
SM
2210 prop = get_keymap (prop, 0, 0);
2211 if (CONSP (prop))
5cae0ec6
RS
2212 return prop;
2213
2b4b027f 2214 if (EQ (type, Qkeymap))
6a7dccef
DL
2215 return Qnil;
2216 else
4b4deea2 2217 return BVAR (buffer, keymap);
5cae0ec6
RS
2218}
2219\f
9c79dd1b 2220/* Produce an interval tree reflecting the intervals in
944d4e4b
KH
2221 TREE from START to START + LENGTH.
2222 The new interval tree has no parent and has a starting-position of 0. */
a50699fd 2223
7b1d5b85 2224INTERVAL
d311d28c 2225copy_intervals (INTERVAL tree, ptrdiff_t start, ptrdiff_t length)
a50699fd
JA
2226{
2227 register INTERVAL i, new, t;
d311d28c 2228 register ptrdiff_t got, prevlen;
a50699fd 2229
77c7bcb1
DA
2230 if (!tree || length <= 0)
2231 return NULL;
a50699fd
JA
2232
2233 i = find_interval (tree, start);
77c7bcb1 2234 eassert (i && LENGTH (i) > 0);
a50699fd 2235
7ce503fd 2236 /* If there is only one interval and it's the default, return nil. */
a50699fd
JA
2237 if ((start - i->position + 1 + length) < LENGTH (i)
2238 && DEFAULT_INTERVAL_P (i))
77c7bcb1 2239 return NULL;
a50699fd
JA
2240
2241 new = make_interval ();
944d4e4b 2242 new->position = 0;
a50699fd 2243 got = (LENGTH (i) - (start - i->position));
9c79dd1b 2244 new->total_length = length;
9c08a8d4 2245 eassert (0 <= TOTAL_LENGTH (new));
a50699fd
JA
2246 copy_properties (i, new);
2247
2248 t = new;
95e3e1ef 2249 prevlen = got;
a50699fd
JA
2250 while (got < length)
2251 {
2252 i = next_interval (i);
2bc7a79b 2253 t = split_interval_right (t, prevlen);
a50699fd 2254 copy_properties (i, t);
95e3e1ef
RS
2255 prevlen = LENGTH (i);
2256 got += prevlen;
a50699fd
JA
2257 }
2258
4314dea4 2259 return balance_an_interval (new);
a50699fd
JA
2260}
2261
7ce503fd 2262/* Give STRING the properties of BUFFER from POSITION to LENGTH. */
a50699fd 2263
09db192c 2264void
e79123aa 2265copy_intervals_to_string (Lisp_Object string, struct buffer *buffer,
d311d28c 2266 ptrdiff_t position, ptrdiff_t length)
a50699fd 2267{
8707c1e5 2268 INTERVAL interval_copy = copy_intervals (buffer_get_intervals (buffer),
a50699fd 2269 position, length);
77c7bcb1 2270 if (!interval_copy)
a50699fd
JA
2271 return;
2272
6a3d20cc 2273 interval_set_object (interval_copy, string);
ad8c997f 2274 string_set_intervals (string, interval_copy);
a50699fd 2275}
d8638d30 2276\f
944d4e4b 2277/* Return 1 if strings S1 and S2 have identical properties; 0 otherwise.
d8638d30
RS
2278 Assume they have identical characters. */
2279
2280int
971de7fb 2281compare_string_intervals (Lisp_Object s1, Lisp_Object s2)
d8638d30
RS
2282{
2283 INTERVAL i1, i2;
d311d28c
PE
2284 ptrdiff_t pos = 0;
2285 ptrdiff_t end = SCHARS (s1);
d8638d30 2286
ad8c997f
DA
2287 i1 = find_interval (string_get_intervals (s1), 0);
2288 i2 = find_interval (string_get_intervals (s2), 0);
d8638d30
RS
2289
2290 while (pos < end)
2291 {
2292 /* Determine how far we can go before we reach the end of I1 or I2. */
d311d28c
PE
2293 ptrdiff_t len1 = (i1 != 0 ? INTERVAL_LAST_POS (i1) : end) - pos;
2294 ptrdiff_t len2 = (i2 != 0 ? INTERVAL_LAST_POS (i2) : end) - pos;
2295 ptrdiff_t distance = min (len1, len2);
d8638d30
RS
2296
2297 /* If we ever find a mismatch between the strings,
2298 they differ. */
2299 if (! intervals_equal (i1, i2))
2300 return 0;
2301
2302 /* Advance POS till the end of the shorter interval,
2303 and advance one or both interval pointers for the new position. */
2304 pos += distance;
2305 if (len1 == distance)
2306 i1 = next_interval (i1);
2307 if (len2 == distance)
2308 i2 = next_interval (i2);
2309 }
2310 return 1;
2311}
37f26f3c 2312\f
37f26f3c
RS
2313/* Recursively adjust interval I in the current buffer
2314 for setting enable_multibyte_characters to MULTI_FLAG.
2315 The range of interval I is START ... END in characters,
2316 START_BYTE ... END_BYTE in bytes. */
2317
2318static void
e79123aa 2319set_intervals_multibyte_1 (INTERVAL i, int multi_flag,
d311d28c
PE
2320 ptrdiff_t start, ptrdiff_t start_byte,
2321 ptrdiff_t end, ptrdiff_t end_byte)
37f26f3c 2322{
37f26f3c
RS
2323 /* Fix the length of this interval. */
2324 if (multi_flag)
2325 i->total_length = end - start;
2326 else
2327 i->total_length = end_byte - start_byte;
9c08a8d4 2328 eassert (0 <= TOTAL_LENGTH (i));
727fec2d
RS
2329
2330 if (TOTAL_LENGTH (i) == 0)
2331 {
2332 delete_interval (i);
2333 return;
2334 }
37f26f3c
RS
2335
2336 /* Recursively fix the length of the subintervals. */
2337 if (i->left)
2338 {
d311d28c 2339 ptrdiff_t left_end, left_end_byte;
37f26f3c
RS
2340
2341 if (multi_flag)
2342 {
d311d28c 2343 ptrdiff_t temp;
37f26f3c
RS
2344 left_end_byte = start_byte + LEFT_TOTAL_LENGTH (i);
2345 left_end = BYTE_TO_CHAR (left_end_byte);
727fec2d
RS
2346
2347 temp = CHAR_TO_BYTE (left_end);
2348
2349 /* If LEFT_END_BYTE is in the middle of a character,
f813361d 2350 adjust it and LEFT_END to a char boundary. */
727fec2d
RS
2351 if (left_end_byte > temp)
2352 {
2353 left_end_byte = temp;
2354 }
2355 if (left_end_byte < temp)
2356 {
2357 left_end--;
2358 left_end_byte = CHAR_TO_BYTE (left_end);
2359 }
37f26f3c
RS
2360 }
2361 else
2362 {
2363 left_end = start + LEFT_TOTAL_LENGTH (i);
2364 left_end_byte = CHAR_TO_BYTE (left_end);
2365 }
2366
2367 set_intervals_multibyte_1 (i->left, multi_flag, start, start_byte,
2368 left_end, left_end_byte);
2369 }
2370 if (i->right)
2371 {
d311d28c 2372 ptrdiff_t right_start_byte, right_start;
37f26f3c
RS
2373
2374 if (multi_flag)
2375 {
d311d28c 2376 ptrdiff_t temp;
727fec2d 2377
37f26f3c
RS
2378 right_start_byte = end_byte - RIGHT_TOTAL_LENGTH (i);
2379 right_start = BYTE_TO_CHAR (right_start_byte);
727fec2d
RS
2380
2381 /* If RIGHT_START_BYTE is in the middle of a character,
f813361d 2382 adjust it and RIGHT_START to a char boundary. */
727fec2d
RS
2383 temp = CHAR_TO_BYTE (right_start);
2384
2385 if (right_start_byte < temp)
2386 {
2387 right_start_byte = temp;
2388 }
2389 if (right_start_byte > temp)
2390 {
2391 right_start++;
2392 right_start_byte = CHAR_TO_BYTE (right_start);
2393 }
37f26f3c
RS
2394 }
2395 else
2396 {
2397 right_start = end - RIGHT_TOTAL_LENGTH (i);
2398 right_start_byte = CHAR_TO_BYTE (right_start);
2399 }
2400
2401 set_intervals_multibyte_1 (i->right, multi_flag,
2402 right_start, right_start_byte,
2403 end, end_byte);
2404 }
727fec2d
RS
2405
2406 /* Rounding to char boundaries can theoretically ake this interval
2407 spurious. If so, delete one child, and copy its property list
2408 to this interval. */
2409 if (LEFT_TOTAL_LENGTH (i) + RIGHT_TOTAL_LENGTH (i) >= TOTAL_LENGTH (i))
2410 {
2411 if ((i)->left)
2412 {
6a3d20cc 2413 interval_set_plist (i, i->left->plist);
727fec2d
RS
2414 (i)->left->total_length = 0;
2415 delete_interval ((i)->left);
2416 }
2417 else
2418 {
6a3d20cc 2419 interval_set_plist (i, i->right->plist);
727fec2d
RS
2420 (i)->right->total_length = 0;
2421 delete_interval ((i)->right);
2422 }
2423 }
37f26f3c 2424}
d2f7a802 2425
24cef261
RS
2426/* Update the intervals of the current buffer
2427 to fit the contents as multibyte (if MULTI_FLAG is 1)
2428 or to fit them as non-multibyte (if MULTI_FLAG is 0). */
2429
2430void
971de7fb 2431set_intervals_multibyte (int multi_flag)
24cef261 2432{
8707c1e5
DA
2433 INTERVAL i = buffer_get_intervals (current_buffer);
2434
2435 if (i)
2436 set_intervals_multibyte_1 (i, multi_flag, BEG, BEG_BYTE, Z, Z_BYTE);
24cef261 2437}