Add 2008 to copyright years.
[bpt/emacs.git] / src / textprop.c
CommitLineData
d418ef42 1/* Interface code for dealing with text properties.
0b5538bd 2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003,
8cabe764 3 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
d418ef42
JA
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
684d6f5b 9the Free Software Foundation; either version 3, or (at your option)
d418ef42
JA
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
4fc5845f
LK
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA. */
d418ef42 21
18160b98 22#include <config.h>
d418ef42
JA
23#include "lisp.h"
24#include "intervals.h"
25#include "buffer.h"
f5957179 26#include "window.h"
59a486ab
RS
27
28#ifndef NULL
29#define NULL (void *)0
30#endif
318d2fa8
RS
31
32/* Test for membership, allowing for t (actually any non-cons) to mean the
33 universal set. */
34
35#define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
d418ef42
JA
36\f
37
38/* NOTES: previous- and next- property change will have to skip
39 zero-length intervals if they are implemented. This could be done
40 inside next_interval and previous_interval.
41
9c79dd1b
JA
42 set_properties needs to deal with the interval property cache.
43
d418ef42 44 It is assumed that for any interval plist, a property appears
d4b530ad 45 only once on the list. Although some code i.e., remove_properties,
d418ef42 46 handles the more general case, the uniqueness of properties is
eb8c3be9 47 necessary for the system to remain consistent. This requirement
cdf3e5a2 48 is enforced by the subrs installing properties onto the intervals. */
d418ef42
JA
49
50\f
cdf3e5a2 51/* Types of hooks. */
d418ef42
JA
52Lisp_Object Qmouse_left;
53Lisp_Object Qmouse_entered;
54Lisp_Object Qpoint_left;
55Lisp_Object Qpoint_entered;
dc70cea7
RS
56Lisp_Object Qcategory;
57Lisp_Object Qlocal_map;
d418ef42 58
cdf3e5a2 59/* Visual properties text (including strings) may have. */
d418ef42 60Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
69bb837e 61Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
19e1c426
RS
62
63/* Sticky properties */
64Lisp_Object Qfront_sticky, Qrear_nonsticky;
d7b4e137
JB
65
66/* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
68 traversing plists. */
70949dac 69#define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
d7b4e137 70
688a5a0f 71Lisp_Object Vinhibit_point_motion_hooks;
ad1b2f20 72Lisp_Object Vdefault_text_properties;
49d110a8 73Lisp_Object Vchar_property_alias_alist;
abc2f676 74Lisp_Object Vtext_property_default_nonsticky;
688a5a0f 75
318d2fa8
RS
76/* verify_interval_modification saves insertion hooks here
77 to be run later by report_interval_modification. */
78Lisp_Object interval_insert_behind_hooks;
79Lisp_Object interval_insert_in_front_hooks;
7cb66899 80
2381d38d
DN
81static void text_read_only P_ ((Lisp_Object)) NO_RETURN;
82
7cb66899
GM
83
84/* Signal a `text-read-only' error. This function makes it easier
85 to capture that error in GDB by putting a breakpoint on it. */
86
87static void
bcf97349
SM
88text_read_only (propval)
89 Lisp_Object propval;
7cb66899 90{
d0a29e1d
KS
91 if (STRINGP (propval))
92 xsignal1 (Qtext_read_only, propval);
93
94 xsignal0 (Qtext_read_only);
7cb66899
GM
95}
96
97
d418ef42 98\f
ac876a79
JA
99/* Extract the interval at the position pointed to by BEGIN from
100 OBJECT, a string or buffer. Additionally, check that the positions
101 pointed to by BEGIN and END are within the bounds of OBJECT, and
102 reverse them if *BEGIN is greater than *END. The objects pointed
103 to by BEGIN and END may be integers or markers; if the latter, they
104 are coerced to integers.
d418ef42 105
d4b530ad
RS
106 When OBJECT is a string, we increment *BEGIN and *END
107 to make them origin-one.
108
d418ef42
JA
109 Note that buffer points don't correspond to interval indices.
110 For example, point-max is 1 greater than the index of the last
111 character. This difference is handled in the caller, which uses
112 the validated points to determine a length, and operates on that.
113 Exceptions are Ftext_properties_at, Fnext_property_change, and
114 Fprevious_property_change which call this function with BEGIN == END.
115 Handle this case specially.
116
117 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
ac876a79
JA
118 create an interval tree for OBJECT if one doesn't exist, provided
119 the object actually contains text. In the current design, if there
d4b530ad 120 is no text, there can be no text properties. */
d418ef42
JA
121
122#define soft 0
123#define hard 1
124
9dd7eec6 125INTERVAL
d418ef42
JA
126validate_interval_range (object, begin, end, force)
127 Lisp_Object object, *begin, *end;
128 int force;
129{
130 register INTERVAL i;
d4b530ad
RS
131 int searchpos;
132
b7826503
PJ
133 CHECK_STRING_OR_BUFFER (object);
134 CHECK_NUMBER_COERCE_MARKER (*begin);
135 CHECK_NUMBER_COERCE_MARKER (*end);
d418ef42
JA
136
137 /* If we are asked for a point, but from a subr which operates
cdf3e5a2 138 on a range, then return nothing. */
64a49ca7 139 if (EQ (*begin, *end) && begin != end)
d418ef42
JA
140 return NULL_INTERVAL;
141
142 if (XINT (*begin) > XINT (*end))
143 {
d4b530ad
RS
144 Lisp_Object n;
145 n = *begin;
d418ef42 146 *begin = *end;
d4b530ad 147 *end = n;
d418ef42
JA
148 }
149
5d2fa46f 150 if (BUFFERP (object))
d418ef42
JA
151 {
152 register struct buffer *b = XBUFFER (object);
153
d418ef42
JA
154 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
155 && XINT (*end) <= BUF_ZV (b)))
156 args_out_of_range (*begin, *end);
866bf246 157 i = BUF_INTERVALS (b);
d418ef42 158
cdf3e5a2 159 /* If there's no text, there are no properties. */
d4b530ad
RS
160 if (BUF_BEGV (b) == BUF_ZV (b))
161 return NULL_INTERVAL;
162
163 searchpos = XINT (*begin);
d418ef42
JA
164 }
165 else
166 {
943afcc7 167 int len = SCHARS (object);
d418ef42 168
d4b530ad 169 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
943afcc7 170 && XINT (*end) <= len))
d418ef42 171 args_out_of_range (*begin, *end);
ad077db0 172 XSETFASTINT (*begin, XFASTINT (*begin));
b1e94638 173 if (begin != end)
ad077db0 174 XSETFASTINT (*end, XFASTINT (*end));
943afcc7 175 i = STRING_INTERVALS (object);
d4b530ad 176
943afcc7 177 if (len == 0)
d4b530ad
RS
178 return NULL_INTERVAL;
179
180 searchpos = XINT (*begin);
d418ef42
JA
181 }
182
183 if (NULL_INTERVAL_P (i))
184 return (force ? create_root_interval (object) : i);
81f6e55f 185
d4b530ad 186 return find_interval (i, searchpos);
d418ef42
JA
187}
188
189/* Validate LIST as a property list. If LIST is not a list, then
190 make one consisting of (LIST nil). Otherwise, verify that LIST
cdf3e5a2 191 is even numbered and thus suitable as a plist. */
d418ef42
JA
192
193static Lisp_Object
194validate_plist (list)
4d780c76 195 Lisp_Object list;
d418ef42
JA
196{
197 if (NILP (list))
198 return Qnil;
199
200 if (CONSP (list))
201 {
202 register int i;
203 register Lisp_Object tail;
204 for (i = 0, tail = list; !NILP (tail); i++)
b1e94638
JB
205 {
206 tail = Fcdr (tail);
207 QUIT;
208 }
d418ef42
JA
209 if (i & 1)
210 error ("Odd length text property list");
211 return list;
212 }
213
214 return Fcons (list, Fcons (Qnil, Qnil));
215}
216
d418ef42 217/* Return nonzero if interval I has all the properties,
cdf3e5a2 218 with the same values, of list PLIST. */
d418ef42
JA
219
220static int
221interval_has_all_properties (plist, i)
222 Lisp_Object plist;
223 INTERVAL i;
224{
695f302f 225 register Lisp_Object tail1, tail2, sym1;
d418ef42
JA
226 register int found;
227
cdf3e5a2 228 /* Go through each element of PLIST. */
d418ef42
JA
229 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
230 {
231 sym1 = Fcar (tail1);
232 found = 0;
233
234 /* Go through I's plist, looking for sym1 */
235 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
236 if (EQ (sym1, Fcar (tail2)))
237 {
238 /* Found the same property on both lists. If the
cdf3e5a2 239 values are unequal, return zero. */
734c51b2 240 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
d418ef42
JA
241 return 0;
242
cdf3e5a2 243 /* Property has same value on both lists; go to next one. */
d418ef42
JA
244 found = 1;
245 break;
246 }
247
248 if (! found)
249 return 0;
250 }
251
252 return 1;
253}
254
255/* Return nonzero if the plist of interval I has any of the
cdf3e5a2 256 properties of PLIST, regardless of their values. */
d418ef42
JA
257
258static INLINE int
259interval_has_some_properties (plist, i)
260 Lisp_Object plist;
261 INTERVAL i;
262{
263 register Lisp_Object tail1, tail2, sym;
264
cdf3e5a2 265 /* Go through each element of PLIST. */
d418ef42
JA
266 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
267 {
268 sym = Fcar (tail1);
269
270 /* Go through i's plist, looking for tail1 */
271 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
272 if (EQ (sym, Fcar (tail2)))
273 return 1;
274 }
275
276 return 0;
277}
11713b6d
RS
278
279/* Return nonzero if the plist of interval I has any of the
280 property names in LIST, regardless of their values. */
281
282static INLINE int
283interval_has_some_properties_list (list, i)
284 Lisp_Object list;
285 INTERVAL i;
286{
287 register Lisp_Object tail1, tail2, sym;
288
289 /* Go through each element of LIST. */
290 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
291 {
292 sym = Fcar (tail1);
293
294 /* Go through i's plist, looking for tail1 */
295 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
296 if (EQ (sym, XCAR (tail2)))
297 return 1;
298 }
299
300 return 0;
301}
d4b530ad 302\f
d7b4e137
JB
303/* Changing the plists of individual intervals. */
304
305/* Return the value of PROP in property-list PLIST, or Qunbound if it
306 has none. */
64a49ca7 307static Lisp_Object
d7b4e137 308property_value (plist, prop)
33ca3504 309 Lisp_Object plist, prop;
d7b4e137
JB
310{
311 Lisp_Object value;
312
313 while (PLIST_ELT_P (plist, value))
70949dac
KR
314 if (EQ (XCAR (plist), prop))
315 return XCAR (value);
d7b4e137 316 else
70949dac 317 plist = XCDR (value);
d7b4e137
JB
318
319 return Qunbound;
320}
321
d4b530ad
RS
322/* Set the properties of INTERVAL to PROPERTIES,
323 and record undo info for the previous values.
324 OBJECT is the string or buffer that INTERVAL belongs to. */
325
326static void
327set_properties (properties, interval, object)
328 Lisp_Object properties, object;
329 INTERVAL interval;
330{
d7b4e137 331 Lisp_Object sym, value;
d4b530ad 332
d7b4e137 333 if (BUFFERP (object))
d4b530ad 334 {
d7b4e137
JB
335 /* For each property in the old plist which is missing from PROPERTIES,
336 or has a different value in PROPERTIES, make an undo record. */
337 for (sym = interval->plist;
338 PLIST_ELT_P (sym, value);
70949dac
KR
339 sym = XCDR (value))
340 if (! EQ (property_value (properties, XCAR (sym)),
341 XCAR (value)))
f7a9275a 342 {
f7a9275a 343 record_property_change (interval->position, LENGTH (interval),
70949dac 344 XCAR (sym), XCAR (value),
f7a9275a
RS
345 object);
346 }
d7b4e137
JB
347
348 /* For each new property that has no value at all in the old plist,
349 make an undo record binding it to nil, so it will be removed. */
350 for (sym = properties;
351 PLIST_ELT_P (sym, value);
70949dac
KR
352 sym = XCDR (value))
353 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
f7a9275a 354 {
f7a9275a 355 record_property_change (interval->position, LENGTH (interval),
70949dac 356 XCAR (sym), Qnil,
f7a9275a
RS
357 object);
358 }
d4b530ad
RS
359 }
360
361 /* Store new properties. */
362 interval->plist = Fcopy_sequence (properties);
363}
d418ef42
JA
364
365/* Add the properties of PLIST to the interval I, or set
366 the value of I's property to the value of the property on PLIST
367 if they are different.
368
d4b530ad
RS
369 OBJECT should be the string or buffer the interval is in.
370
d418ef42
JA
371 Return nonzero if this changes I (i.e., if any members of PLIST
372 are actually added to I's plist) */
373
d4b530ad
RS
374static int
375add_properties (plist, i, object)
d418ef42
JA
376 Lisp_Object plist;
377 INTERVAL i;
d4b530ad 378 Lisp_Object object;
d418ef42 379{
c98da214 380 Lisp_Object tail1, tail2, sym1, val1;
d418ef42
JA
381 register int changed = 0;
382 register int found;
c98da214
RS
383 struct gcpro gcpro1, gcpro2, gcpro3;
384
385 tail1 = plist;
386 sym1 = Qnil;
387 val1 = Qnil;
388 /* No need to protect OBJECT, because we can GC only in the case
389 where it is a buffer, and live buffers are always protected.
390 I and its plist are also protected, via OBJECT. */
391 GCPRO3 (tail1, sym1, val1);
d418ef42 392
cdf3e5a2 393 /* Go through each element of PLIST. */
d418ef42
JA
394 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
395 {
396 sym1 = Fcar (tail1);
397 val1 = Fcar (Fcdr (tail1));
398 found = 0;
399
400 /* Go through I's plist, looking for sym1 */
401 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
402 if (EQ (sym1, Fcar (tail2)))
403 {
c98da214
RS
404 /* No need to gcpro, because tail2 protects this
405 and it must be a cons cell (we get an error otherwise). */
3814ccf5 406 register Lisp_Object this_cdr;
d418ef42 407
3814ccf5 408 this_cdr = Fcdr (tail2);
cdf3e5a2 409 /* Found the property. Now check its value. */
d418ef42
JA
410 found = 1;
411
412 /* The properties have the same value on both lists.
cdf3e5a2 413 Continue to the next property. */
734c51b2 414 if (EQ (val1, Fcar (this_cdr)))
d418ef42
JA
415 break;
416
d4b530ad 417 /* Record this change in the buffer, for undo purposes. */
5d2fa46f 418 if (BUFFERP (object))
d4b530ad 419 {
f7a9275a
RS
420 record_property_change (i->position, LENGTH (i),
421 sym1, Fcar (this_cdr), object);
d4b530ad
RS
422 }
423
d418ef42
JA
424 /* I's property has a different value -- change it */
425 Fsetcar (this_cdr, val1);
426 changed++;
427 break;
428 }
429
430 if (! found)
431 {
d4b530ad 432 /* Record this change in the buffer, for undo purposes. */
5d2fa46f 433 if (BUFFERP (object))
d4b530ad 434 {
f7a9275a
RS
435 record_property_change (i->position, LENGTH (i),
436 sym1, Qnil, object);
d4b530ad 437 }
d418ef42
JA
438 i->plist = Fcons (sym1, Fcons (val1, i->plist));
439 changed++;
440 }
441 }
442
c98da214
RS
443 UNGCPRO;
444
d418ef42
JA
445 return changed;
446}
447
11713b6d
RS
448/* For any members of PLIST, or LIST,
449 which are properties of I, remove them from I's plist.
450 (If PLIST is non-nil, use that, otherwise use LIST.)
d4b530ad 451 OBJECT is the string or buffer containing I. */
d418ef42 452
d4b530ad 453static int
11713b6d
RS
454remove_properties (plist, list, i, object)
455 Lisp_Object plist, list;
d418ef42 456 INTERVAL i;
d4b530ad 457 Lisp_Object object;
d418ef42 458{
3814ccf5 459 register Lisp_Object tail1, tail2, sym, current_plist;
d418ef42
JA
460 register int changed = 0;
461
f25d60d6
KS
462 /* Nonzero means tail1 is a plist, otherwise it is a list. */
463 int use_plist;
11713b6d 464
3814ccf5 465 current_plist = i->plist;
11713b6d
RS
466
467 if (! NILP (plist))
f25d60d6 468 tail1 = plist, use_plist = 1;
11713b6d 469 else
f25d60d6 470 tail1 = list, use_plist = 0;
11713b6d
RS
471
472 /* Go through each element of LIST or PLIST. */
b079118d 473 while (CONSP (tail1))
d418ef42 474 {
11713b6d 475 sym = XCAR (tail1);
d418ef42 476
11713b6d 477 /* First, remove the symbol if it's at the head of the list */
b079118d 478 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
d418ef42 479 {
5d2fa46f 480 if (BUFFERP (object))
11713b6d
RS
481 record_property_change (i->position, LENGTH (i),
482 sym, XCAR (XCDR (current_plist)),
483 object);
d4b530ad 484
11713b6d 485 current_plist = XCDR (XCDR (current_plist));
d418ef42
JA
486 changed++;
487 }
488
11713b6d 489 /* Go through I's plist, looking for SYM. */
d418ef42
JA
490 tail2 = current_plist;
491 while (! NILP (tail2))
492 {
3814ccf5 493 register Lisp_Object this;
11713b6d 494 this = XCDR (XCDR (tail2));
b079118d 495 if (CONSP (this) && EQ (sym, XCAR (this)))
d418ef42 496 {
5d2fa46f 497 if (BUFFERP (object))
11713b6d
RS
498 record_property_change (i->position, LENGTH (i),
499 sym, XCAR (XCDR (this)), object);
d4b530ad 500
11713b6d 501 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
d418ef42
JA
502 changed++;
503 }
504 tail2 = this;
505 }
11713b6d
RS
506
507 /* Advance thru TAIL1 one way or the other. */
f25d60d6
KS
508 tail1 = XCDR (tail1);
509 if (use_plist && CONSP (tail1))
11713b6d 510 tail1 = XCDR (tail1);
d418ef42
JA
511 }
512
513 if (changed)
514 i->plist = current_plist;
515 return changed;
516}
517
d4b530ad 518#if 0
d418ef42 519/* Remove all properties from interval I. Return non-zero
cdf3e5a2 520 if this changes the interval. */
d418ef42
JA
521
522static INLINE int
523erase_properties (i)
524 INTERVAL i;
525{
526 if (NILP (i->plist))
527 return 0;
528
529 i->plist = Qnil;
530 return 1;
531}
d4b530ad 532#endif
d418ef42 533\f
81f6e55f 534/* Returns the interval of POSITION in OBJECT.
cdf3e5a2
RS
535 POSITION is BEG-based. */
536
537INTERVAL
538interval_of (position, object)
539 int position;
540 Lisp_Object object;
541{
542 register INTERVAL i;
543 int beg, end;
544
545 if (NILP (object))
546 XSETBUFFER (object, current_buffer);
d0cb872a
KH
547 else if (EQ (object, Qt))
548 return NULL_INTERVAL;
cdf3e5a2 549
b7826503 550 CHECK_STRING_OR_BUFFER (object);
cdf3e5a2
RS
551
552 if (BUFFERP (object))
553 {
554 register struct buffer *b = XBUFFER (object);
555
556 beg = BUF_BEGV (b);
557 end = BUF_ZV (b);
558 i = BUF_INTERVALS (b);
559 }
560 else
561 {
ad077db0 562 beg = 0;
943afcc7
KR
563 end = SCHARS (object);
564 i = STRING_INTERVALS (object);
cdf3e5a2
RS
565 }
566
567 if (!(beg <= position && position <= end))
c7ef8e24 568 args_out_of_range (make_number (position), make_number (position));
cdf3e5a2
RS
569 if (beg == end || NULL_INTERVAL_P (i))
570 return NULL_INTERVAL;
81f6e55f 571
cdf3e5a2
RS
572 return find_interval (i, position);
573}
574\f
d418ef42
JA
575DEFUN ("text-properties-at", Ftext_properties_at,
576 Stext_properties_at, 1, 2, 0,
8c1a1077 577 doc: /* Return the list of properties of the character at POSITION in OBJECT.
81f6e55f
FP
578If the optional second argument OBJECT is a buffer (or nil, which means
579the current buffer), POSITION is a buffer position (integer or marker).
580If OBJECT is a string, POSITION is a 0-based index into it.
8c1a1077
PJ
581If POSITION is at the end of OBJECT, the value is nil. */)
582 (position, object)
1f5e848a 583 Lisp_Object position, object;
d418ef42
JA
584{
585 register INTERVAL i;
d418ef42
JA
586
587 if (NILP (object))
c8a4fc3d 588 XSETBUFFER (object, current_buffer);
d418ef42 589
1f5e848a 590 i = validate_interval_range (object, &position, &position, soft);
d418ef42
JA
591 if (NULL_INTERVAL_P (i))
592 return Qnil;
1f5e848a 593 /* If POSITION is at the end of the interval,
d4b530ad
RS
594 it means it's the end of OBJECT.
595 There are no properties at the very end,
596 since no character follows. */
1f5e848a 597 if (XINT (position) == LENGTH (i) + i->position)
d4b530ad 598 return Qnil;
d418ef42
JA
599
600 return i->plist;
601}
602
5fbe2a44 603DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
8c1a1077
PJ
604 doc: /* Return the value of POSITION's property PROP, in OBJECT.
605OBJECT is optional and defaults to the current buffer.
606If POSITION is at the end of OBJECT, the value is nil. */)
607 (position, prop, object)
1f5e848a 608 Lisp_Object position, object;
46bb7c2b 609 Lisp_Object prop;
5fbe2a44 610{
1f5e848a 611 return textget (Ftext_properties_at (position, object), prop);
5fbe2a44
RS
612}
613
bcf97349 614/* Return the value of char's property PROP, in OBJECT at POSITION.
8d41abc4
MB
615 OBJECT is optional and defaults to the current buffer.
616 If OVERLAY is non-0, then in the case that the returned property is from
617 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
618 returned in *OVERLAY.
619 If POSITION is at the end of OBJECT, the value is nil.
620 If OBJECT is a buffer, then overlay properties are considered as well as
621 text properties.
622 If OBJECT is a window, then that window's buffer is used, but
623 window-specific overlays are considered only if they are associated
624 with OBJECT. */
625Lisp_Object
626get_char_property_and_overlay (position, prop, object, overlay)
1f5e848a 627 Lisp_Object position, object;
f5957179 628 register Lisp_Object prop;
8d41abc4 629 Lisp_Object *overlay;
f5957179
KH
630{
631 struct window *w = 0;
632
b7826503 633 CHECK_NUMBER_COERCE_MARKER (position);
f5957179
KH
634
635 if (NILP (object))
c8a4fc3d 636 XSETBUFFER (object, current_buffer);
f5957179
KH
637
638 if (WINDOWP (object))
639 {
640 w = XWINDOW (object);
64a49ca7 641 object = w->buffer;
f5957179
KH
642 }
643 if (BUFFERP (object))
644 {
f5957179 645 int noverlays;
b5be4dbe 646 Lisp_Object *overlay_vec;
cbc55f55
RS
647 struct buffer *obuf = current_buffer;
648
649 set_buffer_temp (XBUFFER (object));
f5957179 650
b5be4dbe 651 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
f5957179
KH
652 noverlays = sort_overlays (overlay_vec, noverlays, w);
653
cbc55f55
RS
654 set_buffer_temp (obuf);
655
f5957179
KH
656 /* Now check the overlays in order of decreasing priority. */
657 while (--noverlays >= 0)
658 {
b5be4dbe 659 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
f5957179 660 if (!NILP (tem))
8d41abc4
MB
661 {
662 if (overlay)
663 /* Return the overlay we got the property from. */
664 *overlay = overlay_vec[noverlays];
665 return tem;
666 }
f5957179
KH
667 }
668 }
8d41abc4
MB
669
670 if (overlay)
671 /* Indicate that the return value is not from an overlay. */
672 *overlay = Qnil;
673
f5957179
KH
674 /* Not a buffer, or no appropriate overlay, so fall through to the
675 simpler case. */
8d41abc4
MB
676 return Fget_text_property (position, prop, object);
677}
678
679DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
8c1a1077 680 doc: /* Return the value of POSITION's property PROP, in OBJECT.
8faef085 681Both overlay properties and text properties are checked.
8c1a1077
PJ
682OBJECT is optional and defaults to the current buffer.
683If POSITION is at the end of OBJECT, the value is nil.
684If OBJECT is a buffer, then overlay properties are considered as well as
685text properties.
686If OBJECT is a window, then that window's buffer is used, but window-specific
687overlays are considered only if they are associated with OBJECT. */)
688 (position, prop, object)
8d41abc4
MB
689 Lisp_Object position, object;
690 register Lisp_Object prop;
691{
692 return get_char_property_and_overlay (position, prop, object, 0);
f5957179 693}
97a1bc63
LT
694
695DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
696 Sget_char_property_and_overlay, 2, 3, 0,
697 doc: /* Like `get-char-property', but with extra overlay information.
95555145
RS
698The value is a cons cell. Its car is the return value of `get-char-property'
699with the same arguments--that is, the value of POSITION's property
700PROP in OBJECT. Its cdr is the overlay in which the property was
97a1bc63 701found, or nil, if it was found as a text property or not found at all.
95555145 702
97a1bc63
LT
703OBJECT is optional and defaults to the current buffer. OBJECT may be
704a string, a buffer or a window. For strings, the cdr of the return
705value is always nil, since strings do not have overlays. If OBJECT is
706a window, then that window's buffer is used, but window-specific
707overlays are considered only if they are associated with OBJECT. If
708POSITION is at the end of OBJECT, both car and cdr are nil. */)
709 (position, prop, object)
710 Lisp_Object position, object;
711 register Lisp_Object prop;
712{
713 Lisp_Object overlay;
714 Lisp_Object val
715 = get_char_property_and_overlay (position, prop, object, &overlay);
6519d955 716 return Fcons (val, overlay);
97a1bc63
LT
717}
718
fcab51aa
RS
719\f
720DEFUN ("next-char-property-change", Fnext_char_property_change,
721 Snext_char_property_change, 1, 2, 0,
8c1a1077 722 doc: /* Return the position of next text property or overlay change.
81f6e55f
FP
723This scans characters forward in the current buffer from POSITION till
724it finds a change in some text property, or the beginning or end of an
725overlay, and returns the position of that.
85cc6738 726If none is found up to (point-max), the function returns (point-max).
8c1a1077 727
a41292c2 728If the optional second argument LIMIT is non-nil, don't search
85cc6738
RS
729past position LIMIT; return LIMIT if nothing is found before LIMIT.
730LIMIT is a no-op if it is greater than (point-max). */)
8c1a1077 731 (position, limit)
fcab51aa
RS
732 Lisp_Object position, limit;
733{
734 Lisp_Object temp;
735
736 temp = Fnext_overlay_change (position);
737 if (! NILP (limit))
738 {
d615870a 739 CHECK_NUMBER_COERCE_MARKER (limit);
fcab51aa
RS
740 if (XINT (limit) < XINT (temp))
741 temp = limit;
742 }
743 return Fnext_property_change (position, Qnil, temp);
744}
745
746DEFUN ("previous-char-property-change", Fprevious_char_property_change,
747 Sprevious_char_property_change, 1, 2, 0,
8c1a1077 748 doc: /* Return the position of previous text property or overlay change.
81f6e55f
FP
749Scans characters backward in the current buffer from POSITION till it
750finds a change in some text property, or the beginning or end of an
751overlay, and returns the position of that.
85cc6738 752If none is found since (point-min), the function returns (point-min).
8c1a1077 753
a41292c2 754If the optional second argument LIMIT is non-nil, don't search
85cc6738
RS
755past position LIMIT; return LIMIT if nothing is found before LIMIT.
756LIMIT is a no-op if it is less than (point-min). */)
8c1a1077 757 (position, limit)
fcab51aa
RS
758 Lisp_Object position, limit;
759{
760 Lisp_Object temp;
f5957179 761
fcab51aa
RS
762 temp = Fprevious_overlay_change (position);
763 if (! NILP (limit))
764 {
d615870a 765 CHECK_NUMBER_COERCE_MARKER (limit);
fcab51aa
RS
766 if (XINT (limit) > XINT (temp))
767 temp = limit;
768 }
769 return Fprevious_property_change (position, Qnil, temp);
770}
0b0737d1
GM
771
772
b7e047fb
MB
773DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
774 Snext_single_char_property_change, 2, 4, 0,
8c1a1077
PJ
775 doc: /* Return the position of next text property or overlay change for a specific property.
776Scans characters forward from POSITION till it finds
777a change in the PROP property, then returns the position of the change.
81f6e55f
FP
778If the optional third argument OBJECT is a buffer (or nil, which means
779the current buffer), POSITION is a buffer position (integer or marker).
780If OBJECT is a string, POSITION is a 0-based index into it.
781
85cc6738
RS
782In a string, scan runs to the end of the string.
783In a buffer, it runs to (point-max), and the value cannot exceed that.
784
8c1a1077
PJ
785The property values are compared with `eq'.
786If the property is constant all the way to the end of OBJECT, return the
787last valid position in OBJECT.
788If the optional fourth argument LIMIT is non-nil, don't search
789past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
790 (position, prop, object, limit)
b7e047fb 791 Lisp_Object prop, position, object, limit;
0b0737d1
GM
792{
793 if (STRINGP (object))
794 {
b7e047fb
MB
795 position = Fnext_single_property_change (position, prop, object, limit);
796 if (NILP (position))
0b0737d1
GM
797 {
798 if (NILP (limit))
d5db4077 799 position = make_number (SCHARS (object));
0b0737d1 800 else
d615870a
DK
801 {
802 CHECK_NUMBER (limit);
803 position = limit;
804 }
0b0737d1
GM
805 }
806 }
807 else
808 {
809 Lisp_Object initial_value, value;
aed13378 810 int count = SPECPDL_INDEX ();
0b0737d1 811
b7e047fb 812 if (! NILP (object))
b7826503 813 CHECK_BUFFER (object);
81f6e55f 814
0b0737d1
GM
815 if (BUFFERP (object) && current_buffer != XBUFFER (object))
816 {
817 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
818 Fset_buffer (object);
819 }
820
d615870a
DK
821 CHECK_NUMBER_COERCE_MARKER (position);
822
b7e047fb 823 initial_value = Fget_char_property (position, prop, object);
81f6e55f 824
b7e047fb 825 if (NILP (limit))
85cc6738 826 XSETFASTINT (limit, ZV);
b7e047fb 827 else
b7826503 828 CHECK_NUMBER_COERCE_MARKER (limit);
b7e047fb 829
85cc6738 830 if (XFASTINT (position) >= XFASTINT (limit))
0b0737d1 831 {
85cc6738
RS
832 position = limit;
833 if (XFASTINT (position) > ZV)
834 XSETFASTINT (position, ZV);
0b0737d1 835 }
85cc6738
RS
836 else
837 while (1)
838 {
839 position = Fnext_char_property_change (position, limit);
840 if (XFASTINT (position) >= XFASTINT (limit))
841 {
842 position = limit;
843 break;
844 }
845
846 value = Fget_char_property (position, prop, object);
847 if (!EQ (value, initial_value))
848 break;
849 }
0b0737d1
GM
850
851 unbind_to (count, Qnil);
852 }
853
b7e047fb 854 return position;
0b0737d1
GM
855}
856
b7e047fb
MB
857DEFUN ("previous-single-char-property-change",
858 Fprevious_single_char_property_change,
859 Sprevious_single_char_property_change, 2, 4, 0,
8c1a1077
PJ
860 doc: /* Return the position of previous text property or overlay change for a specific property.
861Scans characters backward from POSITION till it finds
862a change in the PROP property, then returns the position of the change.
81f6e55f
FP
863If the optional third argument OBJECT is a buffer (or nil, which means
864the current buffer), POSITION is a buffer position (integer or marker).
865If OBJECT is a string, POSITION is a 0-based index into it.
866
85cc6738
RS
867In a string, scan runs to the start of the string.
868In a buffer, it runs to (point-min), and the value cannot be less than that.
869
8c1a1077
PJ
870The property values are compared with `eq'.
871If the property is constant all the way to the start of OBJECT, return the
872first valid position in OBJECT.
873If the optional fourth argument LIMIT is non-nil, don't search
874back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
875 (position, prop, object, limit)
b7e047fb
MB
876 Lisp_Object prop, position, object, limit;
877{
878 if (STRINGP (object))
879 {
880 position = Fprevious_single_property_change (position, prop, object, limit);
881 if (NILP (position))
882 {
883 if (NILP (limit))
d5db4077 884 position = make_number (SCHARS (object));
b7e047fb 885 else
d615870a
DK
886 {
887 CHECK_NUMBER (limit);
888 position = limit;
889 }
b7e047fb
MB
890 }
891 }
892 else
893 {
aed13378 894 int count = SPECPDL_INDEX ();
b7e047fb
MB
895
896 if (! NILP (object))
b7826503 897 CHECK_BUFFER (object);
81f6e55f 898
b7e047fb
MB
899 if (BUFFERP (object) && current_buffer != XBUFFER (object))
900 {
901 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
902 Fset_buffer (object);
903 }
81f6e55f 904
d615870a
DK
905 CHECK_NUMBER_COERCE_MARKER (position);
906
b7e047fb 907 if (NILP (limit))
85cc6738 908 XSETFASTINT (limit, BEGV);
b7e047fb 909 else
b7826503 910 CHECK_NUMBER_COERCE_MARKER (limit);
b7e047fb 911
ce6b02e0 912 if (XFASTINT (position) <= XFASTINT (limit))
85cc6738
RS
913 {
914 position = limit;
915 if (XFASTINT (position) < BEGV)
916 XSETFASTINT (position, BEGV);
917 }
ce6b02e0 918 else
b7e047fb 919 {
85cc6738
RS
920 Lisp_Object initial_value
921 = Fget_char_property (make_number (XFASTINT (position) - 1),
922 prop, object);
81f6e55f 923
85cc6738 924 while (1)
ce6b02e0
MB
925 {
926 position = Fprevious_char_property_change (position, limit);
0b0737d1 927
ce6b02e0
MB
928 if (XFASTINT (position) <= XFASTINT (limit))
929 {
930 position = limit;
931 break;
932 }
933 else
934 {
85cc6738
RS
935 Lisp_Object value
936 = Fget_char_property (make_number (XFASTINT (position) - 1),
937 prop, object);
ce6b02e0
MB
938
939 if (!EQ (value, initial_value))
940 break;
941 }
942 }
b7e047fb
MB
943 }
944
945 unbind_to (count, Qnil);
946 }
947
948 return position;
949}
fcab51aa 950\f
d418ef42 951DEFUN ("next-property-change", Fnext_property_change,
111b637d 952 Snext_property_change, 1, 3, 0,
8c1a1077
PJ
953 doc: /* Return the position of next property change.
954Scans characters forward from POSITION in OBJECT till it finds
955a change in some text property, then returns the position of the change.
81f6e55f
FP
956If the optional second argument OBJECT is a buffer (or nil, which means
957the current buffer), POSITION is a buffer position (integer or marker).
958If OBJECT is a string, POSITION is a 0-based index into it.
8c1a1077
PJ
959Return nil if the property is constant all the way to the end of OBJECT.
960If the value is non-nil, it is a position greater than POSITION, never equal.
961
962If the optional third argument LIMIT is non-nil, don't search
963past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
964 (position, object, limit)
1f5e848a 965 Lisp_Object position, object, limit;
d418ef42
JA
966{
967 register INTERVAL i, next;
968
5fbe2a44 969 if (NILP (object))
c8a4fc3d 970 XSETBUFFER (object, current_buffer);
5fbe2a44 971
3a232704 972 if (!NILP (limit) && !EQ (limit, Qt))
b7826503 973 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 974
1f5e848a 975 i = validate_interval_range (object, &position, &position, soft);
d418ef42 976
041aa96f
RS
977 /* If LIMIT is t, return start of next interval--don't
978 bother checking further intervals. */
979 if (EQ (limit, Qt))
980 {
44214c1b
RS
981 if (NULL_INTERVAL_P (i))
982 next = i;
983 else
984 next = next_interval (i);
81f6e55f 985
c7b6dfa6 986 if (NULL_INTERVAL_P (next))
1f5e848a 987 XSETFASTINT (position, (STRINGP (object)
d5db4077 988 ? SCHARS (object)
1f5e848a 989 : BUF_ZV (XBUFFER (object))));
c7b6dfa6 990 else
ad077db0 991 XSETFASTINT (position, next->position);
1f5e848a 992 return position;
041aa96f
RS
993 }
994
44214c1b
RS
995 if (NULL_INTERVAL_P (i))
996 return limit;
997
998 next = next_interval (i);
999
3a232704 1000 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
ad077db0 1001 && (NILP (limit) || next->position < XFASTINT (limit)))
d418ef42
JA
1002 next = next_interval (next);
1003
52c0f270
CY
1004 if (NULL_INTERVAL_P (next)
1005 || (next->position
1006 >= (INTEGERP (limit)
1007 ? XFASTINT (limit)
1008 : (STRINGP (object)
1009 ? SCHARS (object)
1010 : BUF_ZV (XBUFFER (object))))))
111b637d 1011 return limit;
52c0f270
CY
1012 else
1013 return make_number (next->position);
19e1c426
RS
1014}
1015
1016/* Return 1 if there's a change in some property between BEG and END. */
1017
1018int
1019property_change_between_p (beg, end)
1020 int beg, end;
1021{
1022 register INTERVAL i, next;
1023 Lisp_Object object, pos;
1024
c8a4fc3d 1025 XSETBUFFER (object, current_buffer);
e9c4fbcd 1026 XSETFASTINT (pos, beg);
19e1c426
RS
1027
1028 i = validate_interval_range (object, &pos, &pos, soft);
1029 if (NULL_INTERVAL_P (i))
1030 return 0;
1031
1032 next = next_interval (i);
1033 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
1034 {
1035 next = next_interval (next);
e050ef74
RS
1036 if (NULL_INTERVAL_P (next))
1037 return 0;
ad077db0 1038 if (next->position >= end)
19e1c426
RS
1039 return 0;
1040 }
1041
1042 if (NULL_INTERVAL_P (next))
1043 return 0;
1044
1045 return 1;
d418ef42
JA
1046}
1047
9c79dd1b 1048DEFUN ("next-single-property-change", Fnext_single_property_change,
111b637d 1049 Snext_single_property_change, 2, 4, 0,
8c1a1077
PJ
1050 doc: /* Return the position of next property change for a specific property.
1051Scans characters forward from POSITION till it finds
1052a change in the PROP property, then returns the position of the change.
81f6e55f
FP
1053If the optional third argument OBJECT is a buffer (or nil, which means
1054the current buffer), POSITION is a buffer position (integer or marker).
1055If OBJECT is a string, POSITION is a 0-based index into it.
8c1a1077
PJ
1056The property values are compared with `eq'.
1057Return nil if the property is constant all the way to the end of OBJECT.
1058If the value is non-nil, it is a position greater than POSITION, never equal.
1059
1060If the optional fourth argument LIMIT is non-nil, don't search
1061past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1062 (position, prop, object, limit)
1f5e848a 1063 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
1064{
1065 register INTERVAL i, next;
1066 register Lisp_Object here_val;
1067
5fbe2a44 1068 if (NILP (object))
c8a4fc3d 1069 XSETBUFFER (object, current_buffer);
5fbe2a44 1070
1387d54e 1071 if (!NILP (limit))
b7826503 1072 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 1073
1f5e848a 1074 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 1075 if (NULL_INTERVAL_P (i))
111b637d 1076 return limit;
9c79dd1b 1077
6a0486dd 1078 here_val = textget (i->plist, prop);
9c79dd1b 1079 next = next_interval (i);
81f6e55f 1080 while (! NULL_INTERVAL_P (next)
111b637d 1081 && EQ (here_val, textget (next->plist, prop))
ad077db0 1082 && (NILP (limit) || next->position < XFASTINT (limit)))
9c79dd1b
JA
1083 next = next_interval (next);
1084
52c0f270
CY
1085 if (NULL_INTERVAL_P (next)
1086 || (next->position
1087 >= (INTEGERP (limit)
1088 ? XFASTINT (limit)
1089 : (STRINGP (object)
1090 ? SCHARS (object)
1091 : BUF_ZV (XBUFFER (object))))))
111b637d 1092 return limit;
52c0f270
CY
1093 else
1094 return make_number (next->position);
9c79dd1b
JA
1095}
1096
d418ef42 1097DEFUN ("previous-property-change", Fprevious_property_change,
111b637d 1098 Sprevious_property_change, 1, 3, 0,
8c1a1077
PJ
1099 doc: /* Return the position of previous property change.
1100Scans characters backwards from POSITION in OBJECT till it finds
1101a change in some text property, then returns the position of the change.
81f6e55f
FP
1102If the optional second argument OBJECT is a buffer (or nil, which means
1103the current buffer), POSITION is a buffer position (integer or marker).
1104If OBJECT is a string, POSITION is a 0-based index into it.
8c1a1077
PJ
1105Return nil if the property is constant all the way to the start of OBJECT.
1106If the value is non-nil, it is a position less than POSITION, never equal.
1107
1108If the optional third argument LIMIT is non-nil, don't search
1109back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1110 (position, object, limit)
1f5e848a 1111 Lisp_Object position, object, limit;
d418ef42
JA
1112{
1113 register INTERVAL i, previous;
1114
5fbe2a44 1115 if (NILP (object))
c8a4fc3d 1116 XSETBUFFER (object, current_buffer);
5fbe2a44 1117
1387d54e 1118 if (!NILP (limit))
b7826503 1119 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 1120
1f5e848a 1121 i = validate_interval_range (object, &position, &position, soft);
d418ef42 1122 if (NULL_INTERVAL_P (i))
111b637d 1123 return limit;
d418ef42 1124
53b7feec 1125 /* Start with the interval containing the char before point. */
1f5e848a 1126 if (i->position == XFASTINT (position))
53b7feec
RS
1127 i = previous_interval (i);
1128
d418ef42 1129 previous = previous_interval (i);
3a232704 1130 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
111b637d 1131 && (NILP (limit)
ad077db0 1132 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
d418ef42 1133 previous = previous_interval (previous);
d418ef42 1134
52c0f270
CY
1135 if (NULL_INTERVAL_P (previous)
1136 || (previous->position + LENGTH (previous)
1137 <= (INTEGERP (limit)
1138 ? XFASTINT (limit)
1139 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1140 return limit;
1141 else
1142 return make_number (previous->position + LENGTH (previous));
d418ef42
JA
1143}
1144
9c79dd1b 1145DEFUN ("previous-single-property-change", Fprevious_single_property_change,
111b637d 1146 Sprevious_single_property_change, 2, 4, 0,
8c1a1077
PJ
1147 doc: /* Return the position of previous property change for a specific property.
1148Scans characters backward from POSITION till it finds
1149a change in the PROP property, then returns the position of the change.
81f6e55f
FP
1150If the optional third argument OBJECT is a buffer (or nil, which means
1151the current buffer), POSITION is a buffer position (integer or marker).
1152If OBJECT is a string, POSITION is a 0-based index into it.
8c1a1077
PJ
1153The property values are compared with `eq'.
1154Return nil if the property is constant all the way to the start of OBJECT.
1155If the value is non-nil, it is a position less than POSITION, never equal.
1156
1157If the optional fourth argument LIMIT is non-nil, don't search
1158back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1f5e848a
EN
1159 (position, prop, object, limit)
1160 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
1161{
1162 register INTERVAL i, previous;
1163 register Lisp_Object here_val;
1164
5fbe2a44 1165 if (NILP (object))
c8a4fc3d 1166 XSETBUFFER (object, current_buffer);
5fbe2a44 1167
1387d54e 1168 if (!NILP (limit))
b7826503 1169 CHECK_NUMBER_COERCE_MARKER (limit);
1387d54e 1170
1f5e848a 1171 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 1172
53b7feec 1173 /* Start with the interval containing the char before point. */
3a232704 1174 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
53b7feec
RS
1175 i = previous_interval (i);
1176
6873cfa3
KH
1177 if (NULL_INTERVAL_P (i))
1178 return limit;
1179
6a0486dd 1180 here_val = textget (i->plist, prop);
9c79dd1b 1181 previous = previous_interval (i);
3a232704 1182 while (!NULL_INTERVAL_P (previous)
111b637d
RS
1183 && EQ (here_val, textget (previous->plist, prop))
1184 && (NILP (limit)
ad077db0 1185 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
9c79dd1b 1186 previous = previous_interval (previous);
9c79dd1b 1187
52c0f270
CY
1188 if (NULL_INTERVAL_P (previous)
1189 || (previous->position + LENGTH (previous)
1190 <= (INTEGERP (limit)
1191 ? XFASTINT (limit)
1192 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1193 return limit;
1194 else
1195 return make_number (previous->position + LENGTH (previous));
9c79dd1b 1196}
fcab51aa 1197\f
c98da214
RS
1198/* Callers note, this can GC when OBJECT is a buffer (or nil). */
1199
d418ef42 1200DEFUN ("add-text-properties", Fadd_text_properties,
5fbe2a44 1201 Sadd_text_properties, 3, 4, 0,
8c1a1077
PJ
1202 doc: /* Add properties to the text from START to END.
1203The third argument PROPERTIES is a property list
81f6e55f
FP
1204specifying the property values to add. If the optional fourth argument
1205OBJECT is a buffer (or nil, which means the current buffer),
1206START and END are buffer positions (integers or markers).
1207If OBJECT is a string, START and END are 0-based indices into it.
8c1a1077
PJ
1208Return t if any property value actually changed, nil otherwise. */)
1209 (start, end, properties, object)
5fbe2a44 1210 Lisp_Object start, end, properties, object;
d418ef42
JA
1211{
1212 register INTERVAL i, unchanged;
caa31568 1213 register int s, len, modified = 0;
c98da214 1214 struct gcpro gcpro1;
d418ef42
JA
1215
1216 properties = validate_plist (properties);
1217 if (NILP (properties))
1218 return Qnil;
1219
5fbe2a44 1220 if (NILP (object))
c8a4fc3d 1221 XSETBUFFER (object, current_buffer);
5fbe2a44 1222
d418ef42
JA
1223 i = validate_interval_range (object, &start, &end, hard);
1224 if (NULL_INTERVAL_P (i))
1225 return Qnil;
1226
1227 s = XINT (start);
1228 len = XINT (end) - s;
1229
c98da214
RS
1230 /* No need to protect OBJECT, because we GC only if it's a buffer,
1231 and live buffers are always protected. */
1232 GCPRO1 (properties);
1233
d418ef42 1234 /* If we're not starting on an interval boundary, we have to
cdf3e5a2 1235 split this interval. */
d418ef42
JA
1236 if (i->position != s)
1237 {
1238 /* If this interval already has the properties, we can
cdf3e5a2 1239 skip it. */
d418ef42
JA
1240 if (interval_has_all_properties (properties, i))
1241 {
1242 int got = (LENGTH (i) - (s - i->position));
1243 if (got >= len)
64db1307 1244 RETURN_UNGCPRO (Qnil);
d418ef42 1245 len -= got;
05d5b93e 1246 i = next_interval (i);
d418ef42
JA
1247 }
1248 else
1249 {
1250 unchanged = i;
ad9c1940 1251 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 1252 copy_properties (unchanged, i);
d418ef42
JA
1253 }
1254 }
1255
2a631db1 1256 if (BUFFERP (object))
3e145152 1257 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
26c76ace 1258
daa5e28f 1259 /* We are at the beginning of interval I, with LEN chars to scan. */
caa31568 1260 for (;;)
d418ef42 1261 {
d4b530ad
RS
1262 if (i == 0)
1263 abort ();
1264
d418ef42
JA
1265 if (LENGTH (i) >= len)
1266 {
c98da214
RS
1267 /* We can UNGCPRO safely here, because there will be just
1268 one more chance to gc, in the next call to add_properties,
1269 and after that we will not need PROPERTIES or OBJECT again. */
1270 UNGCPRO;
1271
d418ef42 1272 if (interval_has_all_properties (properties, i))
26c76ace 1273 {
2a631db1
RS
1274 if (BUFFERP (object))
1275 signal_after_change (XINT (start), XINT (end) - XINT (start),
1276 XINT (end) - XINT (start));
26c76ace
RS
1277
1278 return modified ? Qt : Qnil;
1279 }
d418ef42
JA
1280
1281 if (LENGTH (i) == len)
1282 {
d4b530ad 1283 add_properties (properties, i, object);
2a631db1
RS
1284 if (BUFFERP (object))
1285 signal_after_change (XINT (start), XINT (end) - XINT (start),
1286 XINT (end) - XINT (start));
d418ef42
JA
1287 return Qt;
1288 }
1289
1290 /* i doesn't have the properties, and goes past the change limit */
1291 unchanged = i;
ad9c1940 1292 i = split_interval_left (unchanged, len);
d418ef42 1293 copy_properties (unchanged, i);
d4b530ad 1294 add_properties (properties, i, object);
2a631db1
RS
1295 if (BUFFERP (object))
1296 signal_after_change (XINT (start), XINT (end) - XINT (start),
1297 XINT (end) - XINT (start));
d418ef42
JA
1298 return Qt;
1299 }
1300
1301 len -= LENGTH (i);
d4b530ad 1302 modified += add_properties (properties, i, object);
d418ef42
JA
1303 i = next_interval (i);
1304 }
1305}
1306
c98da214
RS
1307/* Callers note, this can GC when OBJECT is a buffer (or nil). */
1308
d4b530ad
RS
1309DEFUN ("put-text-property", Fput_text_property,
1310 Sput_text_property, 4, 5, 0,
8c1a1077
PJ
1311 doc: /* Set one property of the text from START to END.
1312The third and fourth arguments PROPERTY and VALUE
1313specify the property to add.
81f6e55f
FP
1314If the optional fifth argument OBJECT is a buffer (or nil, which means
1315the current buffer), START and END are buffer positions (integers or
1316markers). If OBJECT is a string, START and END are 0-based indices into it. */)
8c1a1077 1317 (start, end, property, value, object)
1f5e848a 1318 Lisp_Object start, end, property, value, object;
d4b530ad
RS
1319{
1320 Fadd_text_properties (start, end,
1f5e848a 1321 Fcons (property, Fcons (value, Qnil)),
d4b530ad
RS
1322 object);
1323 return Qnil;
1324}
1325
d418ef42 1326DEFUN ("set-text-properties", Fset_text_properties,
5fbe2a44 1327 Sset_text_properties, 3, 4, 0,
8c1a1077
PJ
1328 doc: /* Completely replace properties of text from START to END.
1329The third argument PROPERTIES is the new property list.
81f6e55f
FP
1330If the optional fourth argument OBJECT is a buffer (or nil, which means
1331the current buffer), START and END are buffer positions (integers or
1332markers). If OBJECT is a string, START and END are 0-based indices into it.
8c1a1077
PJ
1333If PROPERTIES is nil, the effect is to remove all properties from
1334the designated part of OBJECT. */)
1335 (start, end, properties, object)
1f5e848a 1336 Lisp_Object start, end, properties, object;
0087ade6
GM
1337{
1338 return set_text_properties (start, end, properties, object, Qt);
1339}
1340
1341
1342/* Replace properties of text from START to END with new list of
1343 properties PROPERTIES. OBJECT is the buffer or string containing
1344 the text. OBJECT nil means use the current buffer.
1345 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
537562fa
LT
1346 is nil if the function _detected_ that it did not replace any
1347 properties, non-nil otherwise. */
0087ade6
GM
1348
1349Lisp_Object
1350set_text_properties (start, end, properties, object, signal_after_change_p)
1351 Lisp_Object start, end, properties, object, signal_after_change_p;
d418ef42 1352{
28ff4293 1353 register INTERVAL i;
33d7d0df
RS
1354 Lisp_Object ostart, oend;
1355
1356 ostart = start;
1357 oend = end;
d418ef42 1358
1f5e848a 1359 properties = validate_plist (properties);
d418ef42 1360
5fbe2a44 1361 if (NILP (object))
c8a4fc3d 1362 XSETBUFFER (object, current_buffer);
5fbe2a44 1363
919fa9cb
RS
1364 /* If we want no properties for a whole string,
1365 get rid of its intervals. */
1f5e848a 1366 if (NILP (properties) && STRINGP (object)
919fa9cb 1367 && XFASTINT (start) == 0
d5db4077 1368 && XFASTINT (end) == SCHARS (object))
919fa9cb 1369 {
d5db4077 1370 if (! STRING_INTERVALS (object))
537562fa 1371 return Qnil;
26c76ace 1372
9056febe 1373 STRING_SET_INTERVALS (object, NULL_INTERVAL);
919fa9cb
RS
1374 return Qt;
1375 }
1376
facc570e 1377 i = validate_interval_range (object, &start, &end, soft);
919fa9cb 1378
d418ef42 1379 if (NULL_INTERVAL_P (i))
facc570e 1380 {
1f5e848a
EN
1381 /* If buffer has no properties, and we want none, return now. */
1382 if (NILP (properties))
facc570e
RS
1383 return Qnil;
1384
33d7d0df
RS
1385 /* Restore the original START and END values
1386 because validate_interval_range increments them for strings. */
1387 start = ostart;
1388 end = oend;
1389
facc570e
RS
1390 i = validate_interval_range (object, &start, &end, hard);
1391 /* This can return if start == end. */
1392 if (NULL_INTERVAL_P (i))
1393 return Qnil;
1394 }
d418ef42 1395
2a631db1 1396 if (BUFFERP (object))
3e145152 1397 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
26c76ace 1398
78ff4175
RS
1399 set_text_properties_1 (start, end, properties, object, i);
1400
1401 if (BUFFERP (object) && !NILP (signal_after_change_p))
1402 signal_after_change (XINT (start), XINT (end) - XINT (start),
1403 XINT (end) - XINT (start));
1404 return Qt;
1405}
1406
1407/* Replace properties of text from START to END with new list of
1408 properties PROPERTIES. BUFFER is the buffer containing
1409 the text. This does not obey any hooks.
1410 You can provide the interval that START is located in as I,
ce768453 1411 or pass NULL for I and this function will find it.
49f68fd2 1412 START and END can be in any order. */
78ff4175
RS
1413
1414void
1415set_text_properties_1 (start, end, properties, buffer, i)
1416 Lisp_Object start, end, properties, buffer;
1417 INTERVAL i;
1418{
1419 register INTERVAL prev_changed = NULL_INTERVAL;
1420 register int s, len;
1421 INTERVAL unchanged;
1422
1423 s = XINT (start);
1424 len = XINT (end) - s;
49f68fd2
RS
1425 if (len == 0)
1426 return;
1427 if (len < 0)
1428 {
1429 s = s + len;
1430 len = - len;
1431 }
1432
78ff4175
RS
1433 if (i == 0)
1434 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1435
d418ef42
JA
1436 if (i->position != s)
1437 {
1438 unchanged = i;
ad9c1940 1439 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 1440
d418ef42
JA
1441 if (LENGTH (i) > len)
1442 {
9c79dd1b 1443 copy_properties (unchanged, i);
ad9c1940 1444 i = split_interval_left (i, len);
78ff4175
RS
1445 set_properties (properties, i, buffer);
1446 return;
d418ef42
JA
1447 }
1448
78ff4175 1449 set_properties (properties, i, buffer);
daa5e28f 1450
9c79dd1b 1451 if (LENGTH (i) == len)
78ff4175 1452 return;
9c79dd1b
JA
1453
1454 prev_changed = i;
d418ef42
JA
1455 len -= LENGTH (i);
1456 i = next_interval (i);
1457 }
1458
cd7d971d 1459 /* We are starting at the beginning of an interval, I */
7855e674 1460 while (len > 0)
d418ef42 1461 {
d4b530ad
RS
1462 if (i == 0)
1463 abort ();
1464
d418ef42
JA
1465 if (LENGTH (i) >= len)
1466 {
cd7d971d 1467 if (LENGTH (i) > len)
ad9c1940 1468 i = split_interval_left (i, len);
d418ef42 1469
6f232881
RS
1470 /* We have to call set_properties even if we are going to
1471 merge the intervals, so as to make the undo records
1472 and cause redisplay to happen. */
78ff4175 1473 set_properties (properties, i, buffer);
6f232881 1474 if (!NULL_INTERVAL_P (prev_changed))
9c79dd1b 1475 merge_interval_left (i);
78ff4175 1476 return;
d418ef42
JA
1477 }
1478
1479 len -= LENGTH (i);
6f232881
RS
1480
1481 /* We have to call set_properties even if we are going to
1482 merge the intervals, so as to make the undo records
1483 and cause redisplay to happen. */
78ff4175 1484 set_properties (properties, i, buffer);
9c79dd1b 1485 if (NULL_INTERVAL_P (prev_changed))
6f232881 1486 prev_changed = i;
9c79dd1b
JA
1487 else
1488 prev_changed = i = merge_interval_left (i);
1489
d418ef42
JA
1490 i = next_interval (i);
1491 }
d418ef42
JA
1492}
1493
1494DEFUN ("remove-text-properties", Fremove_text_properties,
5fbe2a44 1495 Sremove_text_properties, 3, 4, 0,
8c1a1077
PJ
1496 doc: /* Remove some properties from text from START to END.
1497The third argument PROPERTIES is a property list
1498whose property names specify the properties to remove.
1499\(The values stored in PROPERTIES are ignored.)
81f6e55f
FP
1500If the optional fourth argument OBJECT is a buffer (or nil, which means
1501the current buffer), START and END are buffer positions (integers or
1502markers). If OBJECT is a string, START and END are 0-based indices into it.
1503Return t if any property was actually removed, nil otherwise.
1504
1505Use set-text-properties if you want to remove all text properties. */)
8c1a1077 1506 (start, end, properties, object)
1f5e848a 1507 Lisp_Object start, end, properties, object;
d418ef42
JA
1508{
1509 register INTERVAL i, unchanged;
caa31568 1510 register int s, len, modified = 0;
d418ef42 1511
5fbe2a44 1512 if (NILP (object))
c8a4fc3d 1513 XSETBUFFER (object, current_buffer);
5fbe2a44 1514
d418ef42
JA
1515 i = validate_interval_range (object, &start, &end, soft);
1516 if (NULL_INTERVAL_P (i))
1517 return Qnil;
1518
1519 s = XINT (start);
1520 len = XINT (end) - s;
9c79dd1b 1521
d418ef42
JA
1522 if (i->position != s)
1523 {
1524 /* No properties on this first interval -- return if
cdf3e5a2 1525 it covers the entire region. */
1f5e848a 1526 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1527 {
1528 int got = (LENGTH (i) - (s - i->position));
1529 if (got >= len)
1530 return Qnil;
1531 len -= got;
05d5b93e 1532 i = next_interval (i);
d418ef42 1533 }
daa5e28f
RS
1534 /* Split away the beginning of this interval; what we don't
1535 want to modify. */
d418ef42
JA
1536 else
1537 {
1538 unchanged = i;
ad9c1940 1539 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 1540 copy_properties (unchanged, i);
d418ef42
JA
1541 }
1542 }
1543
2a631db1 1544 if (BUFFERP (object))
3e145152 1545 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
26c76ace 1546
d418ef42 1547 /* We are at the beginning of an interval, with len to scan */
caa31568 1548 for (;;)
d418ef42 1549 {
d4b530ad
RS
1550 if (i == 0)
1551 abort ();
1552
d418ef42
JA
1553 if (LENGTH (i) >= len)
1554 {
1f5e848a 1555 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1556 return modified ? Qt : Qnil;
1557
1558 if (LENGTH (i) == len)
1559 {
11713b6d
RS
1560 remove_properties (properties, Qnil, i, object);
1561 if (BUFFERP (object))
1562 signal_after_change (XINT (start), XINT (end) - XINT (start),
1563 XINT (end) - XINT (start));
1564 return Qt;
1565 }
1566
1567 /* i has the properties, and goes past the change limit */
1568 unchanged = i;
1569 i = split_interval_left (i, len);
1570 copy_properties (unchanged, i);
1571 remove_properties (properties, Qnil, i, object);
1572 if (BUFFERP (object))
1573 signal_after_change (XINT (start), XINT (end) - XINT (start),
1574 XINT (end) - XINT (start));
1575 return Qt;
1576 }
1577
1578 len -= LENGTH (i);
1579 modified += remove_properties (properties, Qnil, i, object);
1580 i = next_interval (i);
1581 }
1582}
1583
1584DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1585 Sremove_list_of_text_properties, 3, 4, 0,
1586 doc: /* Remove some properties from text from START to END.
1587The third argument LIST-OF-PROPERTIES is a list of property names to remove.
81f6e55f
FP
1588If the optional fourth argument OBJECT is a buffer (or nil, which means
1589the current buffer), START and END are buffer positions (integers or
1590markers). If OBJECT is a string, START and END are 0-based indices into it.
11713b6d
RS
1591Return t if any property was actually removed, nil otherwise. */)
1592 (start, end, list_of_properties, object)
1593 Lisp_Object start, end, list_of_properties, object;
1594{
1595 register INTERVAL i, unchanged;
1596 register int s, len, modified = 0;
1597 Lisp_Object properties;
1598 properties = list_of_properties;
1599
1600 if (NILP (object))
1601 XSETBUFFER (object, current_buffer);
1602
1603 i = validate_interval_range (object, &start, &end, soft);
1604 if (NULL_INTERVAL_P (i))
1605 return Qnil;
1606
1607 s = XINT (start);
1608 len = XINT (end) - s;
1609
1610 if (i->position != s)
1611 {
1612 /* No properties on this first interval -- return if
1613 it covers the entire region. */
1614 if (! interval_has_some_properties_list (properties, i))
1615 {
1616 int got = (LENGTH (i) - (s - i->position));
1617 if (got >= len)
1618 return Qnil;
1619 len -= got;
1620 i = next_interval (i);
1621 }
1622 /* Split away the beginning of this interval; what we don't
1623 want to modify. */
1624 else
1625 {
1626 unchanged = i;
1627 i = split_interval_right (unchanged, s - unchanged->position);
1628 copy_properties (unchanged, i);
1629 }
1630 }
1631
9b17c9f5
LH
1632 /* We are at the beginning of an interval, with len to scan.
1633 The flag `modified' records if changes have been made.
1634 When object is a buffer, we must call modify_region before changes are
1635 made and signal_after_change when we are done.
e0f24100
GM
1636 We call modify_region before calling remove_properties if modified == 0,
1637 and we call signal_after_change before returning if modified != 0. */
11713b6d
RS
1638 for (;;)
1639 {
1640 if (i == 0)
1641 abort ();
1642
1643 if (LENGTH (i) >= len)
1644 {
1645 if (! interval_has_some_properties_list (properties, i))
9b17c9f5
LH
1646 if (modified)
1647 {
1648 if (BUFFERP (object))
1649 signal_after_change (XINT (start), XINT (end) - XINT (start),
1650 XINT (end) - XINT (start));
1651 return Qt;
1652 }
1653 else
1654 return Qnil;
11713b6d
RS
1655
1656 if (LENGTH (i) == len)
1657 {
9b17c9f5 1658 if (!modified && BUFFERP (object))
3e145152 1659 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
11713b6d 1660 remove_properties (Qnil, properties, i, object);
2a631db1
RS
1661 if (BUFFERP (object))
1662 signal_after_change (XINT (start), XINT (end) - XINT (start),
1663 XINT (end) - XINT (start));
d418ef42
JA
1664 return Qt;
1665 }
1666
1667 /* i has the properties, and goes past the change limit */
daa5e28f 1668 unchanged = i;
ad9c1940 1669 i = split_interval_left (i, len);
d418ef42 1670 copy_properties (unchanged, i);
9b17c9f5 1671 if (!modified && BUFFERP (object))
3e145152 1672 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
11713b6d 1673 remove_properties (Qnil, properties, i, object);
2a631db1
RS
1674 if (BUFFERP (object))
1675 signal_after_change (XINT (start), XINT (end) - XINT (start),
1676 XINT (end) - XINT (start));
d418ef42
JA
1677 return Qt;
1678 }
1679
9b17c9f5
LH
1680 if (interval_has_some_properties_list (properties, i))
1681 {
1682 if (!modified && BUFFERP (object))
3e145152 1683 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
9b17c9f5
LH
1684 remove_properties (Qnil, properties, i, object);
1685 modified = 1;
1686 }
d418ef42 1687 len -= LENGTH (i);
d418ef42
JA
1688 i = next_interval (i);
1689 }
1690}
fcab51aa 1691\f
ad9c1940
JB
1692DEFUN ("text-property-any", Ftext_property_any,
1693 Stext_property_any, 4, 5, 0,
8c1a1077
PJ
1694 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1695If so, return the position of the first character whose property PROPERTY
1696is `eq' to VALUE. Otherwise return nil.
81f6e55f
FP
1697If the optional fifth argument OBJECT is a buffer (or nil, which means
1698the current buffer), START and END are buffer positions (integers or
1699markers). If OBJECT is a string, START and END are 0-based indices into it. */)
8c1a1077
PJ
1700 (start, end, property, value, object)
1701 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1702{
1703 register INTERVAL i;
1704 register int e, pos;
1705
1706 if (NILP (object))
c8a4fc3d 1707 XSETBUFFER (object, current_buffer);
ad9c1940 1708 i = validate_interval_range (object, &start, &end, soft);
2084fddb
KH
1709 if (NULL_INTERVAL_P (i))
1710 return (!NILP (value) || EQ (start, end) ? Qnil : start);
ad9c1940
JB
1711 e = XINT (end);
1712
1713 while (! NULL_INTERVAL_P (i))
1714 {
1715 if (i->position >= e)
1716 break;
1f5e848a 1717 if (EQ (textget (i->plist, property), value))
ad9c1940
JB
1718 {
1719 pos = i->position;
1720 if (pos < XINT (start))
1721 pos = XINT (start);
ad077db0 1722 return make_number (pos);
ad9c1940
JB
1723 }
1724 i = next_interval (i);
1725 }
1726 return Qnil;
1727}
1728
1729DEFUN ("text-property-not-all", Ftext_property_not_all,
1730 Stext_property_not_all, 4, 5, 0,
8c1a1077
PJ
1731 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1732If so, return the position of the first character whose property PROPERTY
1733is not `eq' to VALUE. Otherwise, return nil.
81f6e55f
FP
1734If the optional fifth argument OBJECT is a buffer (or nil, which means
1735the current buffer), START and END are buffer positions (integers or
1736markers). If OBJECT is a string, START and END are 0-based indices into it. */)
8c1a1077
PJ
1737 (start, end, property, value, object)
1738 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1739{
1740 register INTERVAL i;
1741 register int s, e;
1742
1743 if (NILP (object))
c8a4fc3d 1744 XSETBUFFER (object, current_buffer);
ad9c1940
JB
1745 i = validate_interval_range (object, &start, &end, soft);
1746 if (NULL_INTERVAL_P (i))
916a3119 1747 return (NILP (value) || EQ (start, end)) ? Qnil : start;
ad9c1940
JB
1748 s = XINT (start);
1749 e = XINT (end);
1750
1751 while (! NULL_INTERVAL_P (i))
1752 {
1753 if (i->position >= e)
1754 break;
1f5e848a 1755 if (! EQ (textget (i->plist, property), value))
ad9c1940
JB
1756 {
1757 if (i->position > s)
1758 s = i->position;
ad077db0 1759 return make_number (s);
ad9c1940
JB
1760 }
1761 i = next_interval (i);
1762 }
1763 return Qnil;
1764}
e138dfdc
MB
1765
1766\f
1767/* Return the direction from which the text-property PROP would be
1768 inherited by any new text inserted at POS: 1 if it would be
1769 inherited from the char after POS, -1 if it would be inherited from
6f716644
SM
1770 the char before POS, and 0 if from neither.
1771 BUFFER can be either a buffer or nil (meaning current buffer). */
e138dfdc
MB
1772
1773int
6f716644
SM
1774text_property_stickiness (prop, pos, buffer)
1775 Lisp_Object prop, pos, buffer;
e138dfdc
MB
1776{
1777 Lisp_Object prev_pos, front_sticky;
1778 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1779
6f716644
SM
1780 if (NILP (buffer))
1781 XSETBUFFER (buffer, current_buffer);
1782
1783 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
e138dfdc
MB
1784 /* Consider previous character. */
1785 {
1786 Lisp_Object rear_non_sticky;
1787
1788 prev_pos = make_number (XINT (pos) - 1);
6f716644 1789 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
e138dfdc
MB
1790
1791 if (!NILP (CONSP (rear_non_sticky)
1792 ? Fmemq (prop, rear_non_sticky)
1793 : rear_non_sticky))
1794 /* PROP is rear-non-sticky. */
1795 is_rear_sticky = 0;
1796 }
506d2f9a
CY
1797 else
1798 return 0;
e138dfdc
MB
1799
1800 /* Consider following character. */
506d2f9a
CY
1801 /* This signals an arg-out-of-range error if pos is outside the
1802 buffer's accessible range. */
6f716644 1803 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
e138dfdc
MB
1804
1805 if (EQ (front_sticky, Qt)
1806 || (CONSP (front_sticky)
1807 && !NILP (Fmemq (prop, front_sticky))))
1808 /* PROP is inherited from after. */
1809 is_front_sticky = 1;
1810
1811 /* Simple cases, where the properties are consistent. */
1812 if (is_rear_sticky && !is_front_sticky)
1813 return -1;
1814 else if (!is_rear_sticky && is_front_sticky)
1815 return 1;
1816 else if (!is_rear_sticky && !is_front_sticky)
1817 return 0;
1818
1819 /* The stickiness properties are inconsistent, so we have to
1820 disambiguate. Basically, rear-sticky wins, _except_ if the
1821 property that would be inherited has a value of nil, in which case
1822 front-sticky wins. */
6f716644
SM
1823 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1824 || NILP (Fget_text_property (prev_pos, prop, buffer)))
e138dfdc
MB
1825 return 1;
1826 else
1827 return -1;
1828}
1829
fcab51aa 1830\f
15e4954b
JB
1831/* I don't think this is the right interface to export; how often do you
1832 want to do something like this, other than when you're copying objects
1833 around?
1834
1835 I think it would be better to have a pair of functions, one which
1836 returns the text properties of a region as a list of ranges and
1837 plists, and another which applies such a list to another object. */
1838
c98da214
RS
1839/* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1840 SRC and DEST may each refer to strings or buffers.
1841 Optional sixth argument PROP causes only that property to be copied.
1842 Properties are copied to DEST as if by `add-text-properties'.
1843 Return t if any property value actually changed, nil otherwise. */
1844
1845/* Note this can GC when DEST is a buffer. */
ad077db0 1846
15e4954b
JB
1847Lisp_Object
1848copy_text_properties (start, end, src, pos, dest, prop)
1849 Lisp_Object start, end, src, pos, dest, prop;
1850{
1851 INTERVAL i;
1852 Lisp_Object res;
1853 Lisp_Object stuff;
1854 Lisp_Object plist;
1855 int s, e, e2, p, len, modified = 0;
c98da214 1856 struct gcpro gcpro1, gcpro2;
15e4954b
JB
1857
1858 i = validate_interval_range (src, &start, &end, soft);
1859 if (NULL_INTERVAL_P (i))
1860 return Qnil;
1861
b7826503 1862 CHECK_NUMBER_COERCE_MARKER (pos);
15e4954b
JB
1863 {
1864 Lisp_Object dest_start, dest_end;
1865
1866 dest_start = pos;
e9c4fbcd 1867 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
15e4954b
JB
1868 /* Apply this to a copy of pos; it will try to increment its arguments,
1869 which we don't want. */
1870 validate_interval_range (dest, &dest_start, &dest_end, soft);
1871 }
1872
1873 s = XINT (start);
1874 e = XINT (end);
1875 p = XINT (pos);
1876
1877 stuff = Qnil;
1878
1879 while (s < e)
1880 {
1881 e2 = i->position + LENGTH (i);
1882 if (e2 > e)
1883 e2 = e;
1884 len = e2 - s;
1885
1886 plist = i->plist;
1887 if (! NILP (prop))
1888 while (! NILP (plist))
1889 {
1890 if (EQ (Fcar (plist), prop))
1891 {
1892 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1893 break;
1894 }
1895 plist = Fcdr (Fcdr (plist));
1896 }
1897 if (! NILP (plist))
1898 {
1899 /* Must defer modifications to the interval tree in case src
cdf3e5a2 1900 and dest refer to the same string or buffer. */
15e4954b
JB
1901 stuff = Fcons (Fcons (make_number (p),
1902 Fcons (make_number (p + len),
1903 Fcons (plist, Qnil))),
1904 stuff);
1905 }
1906
1907 i = next_interval (i);
1908 if (NULL_INTERVAL_P (i))
1909 break;
1910
1911 p += len;
1912 s = i->position;
1913 }
1914
c98da214
RS
1915 GCPRO2 (stuff, dest);
1916
15e4954b
JB
1917 while (! NILP (stuff))
1918 {
1919 res = Fcar (stuff);
1920 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1921 Fcar (Fcdr (Fcdr (res))), dest);
1922 if (! NILP (res))
1923 modified++;
1924 stuff = Fcdr (stuff);
1925 }
1926
c98da214
RS
1927 UNGCPRO;
1928
15e4954b
JB
1929 return modified ? Qt : Qnil;
1930}
9dd7eec6
GM
1931
1932
1933/* Return a list representing the text properties of OBJECT between
1934 START and END. if PROP is non-nil, report only on that property.
1935 Each result list element has the form (S E PLIST), where S and E
1936 are positions in OBJECT and PLIST is a property list containing the
1937 text properties of OBJECT between S and E. Value is nil if OBJECT
1938 doesn't contain text properties between START and END. */
1939
1940Lisp_Object
1941text_property_list (object, start, end, prop)
1942 Lisp_Object object, start, end, prop;
1943{
1944 struct interval *i;
1945 Lisp_Object result;
9dd7eec6
GM
1946
1947 result = Qnil;
81f6e55f 1948
9dd7eec6
GM
1949 i = validate_interval_range (object, &start, &end, soft);
1950 if (!NULL_INTERVAL_P (i))
1951 {
1952 int s = XINT (start);
1953 int e = XINT (end);
81f6e55f 1954
9dd7eec6
GM
1955 while (s < e)
1956 {
1957 int interval_end, len;
1958 Lisp_Object plist;
81f6e55f 1959
9dd7eec6
GM
1960 interval_end = i->position + LENGTH (i);
1961 if (interval_end > e)
1962 interval_end = e;
1963 len = interval_end - s;
81f6e55f 1964
9dd7eec6
GM
1965 plist = i->plist;
1966
1967 if (!NILP (prop))
1968 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1969 if (EQ (Fcar (plist), prop))
1970 {
1971 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1972 break;
1973 }
1974
1975 if (!NILP (plist))
1976 result = Fcons (Fcons (make_number (s),
1977 Fcons (make_number (s + len),
1978 Fcons (plist, Qnil))),
1979 result);
81f6e55f 1980
9dd7eec6
GM
1981 i = next_interval (i);
1982 if (NULL_INTERVAL_P (i))
1983 break;
1984 s = i->position;
1985 }
1986 }
81f6e55f 1987
9dd7eec6
GM
1988 return result;
1989}
1990
1991
1992/* Add text properties to OBJECT from LIST. LIST is a list of triples
1993 (START END PLIST), where START and END are positions and PLIST is a
1994 property list containing the text properties to add. Adjust START
1995 and END positions by DELTA before adding properties. Value is
1996 non-zero if OBJECT was modified. */
1997
1998int
1999add_text_properties_from_list (object, list, delta)
2000 Lisp_Object object, list, delta;
2001{
2002 struct gcpro gcpro1, gcpro2;
2003 int modified_p = 0;
81f6e55f 2004
9dd7eec6 2005 GCPRO2 (list, object);
81f6e55f 2006
9dd7eec6
GM
2007 for (; CONSP (list); list = XCDR (list))
2008 {
2009 Lisp_Object item, start, end, plist, tem;
81f6e55f 2010
9dd7eec6
GM
2011 item = XCAR (list);
2012 start = make_number (XINT (XCAR (item)) + XINT (delta));
2013 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
2014 plist = XCAR (XCDR (XCDR (item)));
81f6e55f 2015
9dd7eec6
GM
2016 tem = Fadd_text_properties (start, end, plist, object);
2017 if (!NILP (tem))
2018 modified_p = 1;
2019 }
2020
2021 UNGCPRO;
2022 return modified_p;
2023}
2024
2025
2026
2027/* Modify end-points of ranges in LIST destructively. LIST is a list
2028 as returned from text_property_list. Change end-points equal to
2029 OLD_END to NEW_END. */
2030
2031void
2032extend_property_ranges (list, old_end, new_end)
2033 Lisp_Object list, old_end, new_end;
2034{
2035 for (; CONSP (list); list = XCDR (list))
2036 {
2037 Lisp_Object item, end;
81f6e55f 2038
9dd7eec6
GM
2039 item = XCAR (list);
2040 end = XCAR (XCDR (item));
2041
2042 if (EQ (end, old_end))
f3fbd155 2043 XSETCAR (XCDR (item), new_end);
9dd7eec6
GM
2044 }
2045}
2046
2047
318d2fa8
RS
2048\f
2049/* Call the modification hook functions in LIST, each with START and END. */
2050
2051static void
2052call_mod_hooks (list, start, end)
2053 Lisp_Object list, start, end;
2054{
2055 struct gcpro gcpro1;
2056 GCPRO1 (list);
2057 while (!NILP (list))
2058 {
2059 call2 (Fcar (list), start, end);
2060 list = Fcdr (list);
2061 }
2062 UNGCPRO;
2063}
2064
96f90544
RS
2065/* Check for read-only intervals between character positions START ... END,
2066 in BUF, and signal an error if we find one.
2067
2068 Then check for any modification hooks in the range.
2069 Create a list of all these hooks in lexicographic order,
2070 eliminating consecutive extra copies of the same hook. Then call
2071 those hooks in order, with START and END - 1 as arguments. */
15e4954b 2072
318d2fa8
RS
2073void
2074verify_interval_modification (buf, start, end)
2075 struct buffer *buf;
2076 int start, end;
2077{
2078 register INTERVAL intervals = BUF_INTERVALS (buf);
695f302f 2079 register INTERVAL i;
318d2fa8
RS
2080 Lisp_Object hooks;
2081 register Lisp_Object prev_mod_hooks;
2082 Lisp_Object mod_hooks;
2083 struct gcpro gcpro1;
2084
2085 hooks = Qnil;
2086 prev_mod_hooks = Qnil;
2087 mod_hooks = Qnil;
2088
2089 interval_insert_behind_hooks = Qnil;
2090 interval_insert_in_front_hooks = Qnil;
2091
2092 if (NULL_INTERVAL_P (intervals))
2093 return;
2094
2095 if (start > end)
2096 {
2097 int temp = start;
2098 start = end;
2099 end = temp;
2100 }
2101
2102 /* For an insert operation, check the two chars around the position. */
2103 if (start == end)
2104 {
7cb66899 2105 INTERVAL prev = NULL;
318d2fa8
RS
2106 Lisp_Object before, after;
2107
2108 /* Set I to the interval containing the char after START,
2109 and PREV to the interval containing the char before START.
2110 Either one may be null. They may be equal. */
2111 i = find_interval (intervals, start);
2112
2113 if (start == BUF_BEGV (buf))
2114 prev = 0;
2115 else if (i->position == start)
2116 prev = previous_interval (i);
2117 else if (i->position < start)
2118 prev = i;
2119 if (start == BUF_ZV (buf))
2120 i = 0;
2121
2122 /* If Vinhibit_read_only is set and is not a list, we can
2123 skip the read_only checks. */
2124 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2125 {
2126 /* If I and PREV differ we need to check for the read-only
cdf3e5a2 2127 property together with its stickiness. If either I or
318d2fa8
RS
2128 PREV are 0, this check is all we need.
2129 We have to take special care, since read-only may be
2130 indirectly defined via the category property. */
2131 if (i != prev)
2132 {
2133 if (! NULL_INTERVAL_P (i))
2134 {
2135 after = textget (i->plist, Qread_only);
81f6e55f 2136
318d2fa8
RS
2137 /* If interval I is read-only and read-only is
2138 front-sticky, inhibit insertion.
2139 Check for read-only as well as category. */
2140 if (! NILP (after)
2141 && NILP (Fmemq (after, Vinhibit_read_only)))
2142 {
2143 Lisp_Object tem;
2144
2145 tem = textget (i->plist, Qfront_sticky);
2146 if (TMEM (Qread_only, tem)
2147 || (NILP (Fplist_get (i->plist, Qread_only))
2148 && TMEM (Qcategory, tem)))
bcf97349 2149 text_read_only (after);
318d2fa8
RS
2150 }
2151 }
2152
2153 if (! NULL_INTERVAL_P (prev))
2154 {
2155 before = textget (prev->plist, Qread_only);
81f6e55f 2156
318d2fa8
RS
2157 /* If interval PREV is read-only and read-only isn't
2158 rear-nonsticky, inhibit insertion.
2159 Check for read-only as well as category. */
2160 if (! NILP (before)
2161 && NILP (Fmemq (before, Vinhibit_read_only)))
2162 {
2163 Lisp_Object tem;
2164
2165 tem = textget (prev->plist, Qrear_nonsticky);
2166 if (! TMEM (Qread_only, tem)
2167 && (! NILP (Fplist_get (prev->plist,Qread_only))
2168 || ! TMEM (Qcategory, tem)))
bcf97349 2169 text_read_only (before);
318d2fa8
RS
2170 }
2171 }
2172 }
2173 else if (! NULL_INTERVAL_P (i))
2174 {
2175 after = textget (i->plist, Qread_only);
81f6e55f 2176
318d2fa8
RS
2177 /* If interval I is read-only and read-only is
2178 front-sticky, inhibit insertion.
2179 Check for read-only as well as category. */
2180 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2181 {
2182 Lisp_Object tem;
2183
2184 tem = textget (i->plist, Qfront_sticky);
2185 if (TMEM (Qread_only, tem)
2186 || (NILP (Fplist_get (i->plist, Qread_only))
2187 && TMEM (Qcategory, tem)))
bcf97349 2188 text_read_only (after);
318d2fa8
RS
2189
2190 tem = textget (prev->plist, Qrear_nonsticky);
2191 if (! TMEM (Qread_only, tem)
2192 && (! NILP (Fplist_get (prev->plist, Qread_only))
2193 || ! TMEM (Qcategory, tem)))
bcf97349 2194 text_read_only (after);
318d2fa8
RS
2195 }
2196 }
2197 }
2198
2199 /* Run both insert hooks (just once if they're the same). */
2200 if (!NULL_INTERVAL_P (prev))
2201 interval_insert_behind_hooks
2202 = textget (prev->plist, Qinsert_behind_hooks);
2203 if (!NULL_INTERVAL_P (i))
2204 interval_insert_in_front_hooks
2205 = textget (i->plist, Qinsert_in_front_hooks);
2206 }
0ba7995b 2207 else
318d2fa8
RS
2208 {
2209 /* Loop over intervals on or next to START...END,
2210 collecting their hooks. */
2211
2212 i = find_interval (intervals, start);
2213 do
2214 {
2215 if (! INTERVAL_WRITABLE_P (i))
bcf97349 2216 text_read_only (textget (i->plist, Qread_only));
318d2fa8 2217
0ba7995b 2218 if (!inhibit_modification_hooks)
318d2fa8 2219 {
0ba7995b
GM
2220 mod_hooks = textget (i->plist, Qmodification_hooks);
2221 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2222 {
2223 hooks = Fcons (mod_hooks, hooks);
2224 prev_mod_hooks = mod_hooks;
2225 }
318d2fa8
RS
2226 }
2227
2228 i = next_interval (i);
2229 }
2230 /* Keep going thru the interval containing the char before END. */
2231 while (! NULL_INTERVAL_P (i) && i->position < end);
2232
0ba7995b 2233 if (!inhibit_modification_hooks)
318d2fa8 2234 {
0ba7995b
GM
2235 GCPRO1 (hooks);
2236 hooks = Fnreverse (hooks);
2237 while (! EQ (hooks, Qnil))
2238 {
2239 call_mod_hooks (Fcar (hooks), make_number (start),
2240 make_number (end));
2241 hooks = Fcdr (hooks);
2242 }
2243 UNGCPRO;
318d2fa8 2244 }
318d2fa8
RS
2245 }
2246}
2247
96f90544 2248/* Run the interval hooks for an insertion on character range START ... END.
318d2fa8
RS
2249 verify_interval_modification chose which hooks to run;
2250 this function is called after the insertion happens
2251 so it can indicate the range of inserted text. */
2252
2253void
2254report_interval_modification (start, end)
2255 Lisp_Object start, end;
2256{
2257 if (! NILP (interval_insert_behind_hooks))
2e34157c 2258 call_mod_hooks (interval_insert_behind_hooks, start, end);
318d2fa8
RS
2259 if (! NILP (interval_insert_in_front_hooks)
2260 && ! EQ (interval_insert_in_front_hooks,
2261 interval_insert_behind_hooks))
2e34157c 2262 call_mod_hooks (interval_insert_in_front_hooks, start, end);
318d2fa8
RS
2263}
2264\f
d418ef42
JA
2265void
2266syms_of_textprop ()
2267{
ad1b2f20 2268 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
8c1a1077
PJ
2269 doc: /* Property-list used as default values.
2270The value of a property in this list is seen as the value for every
2271character that does not have its own value for that property. */);
ad1b2f20 2272 Vdefault_text_properties = Qnil;
c7dd82a3 2273
49d110a8
CW
2274 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2275 doc: /* Alist of alternative properties for properties without a value.
2276Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2277If a piece of text has no direct value for a particular property, then
2278this alist is consulted. If that property appears in the alist, then
2279the first non-nil value from the associated alternative properties is
2280returned. */);
2281 Vchar_property_alias_alist = Qnil;
2282
688a5a0f 2283 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
8c1a1077
PJ
2284 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2285This also inhibits the use of the `intangible' text property. */);
688a5a0f 2286 Vinhibit_point_motion_hooks = Qnil;
318d2fa8 2287
abc2f676
KH
2288 DEFVAR_LISP ("text-property-default-nonsticky",
2289 &Vtext_property_default_nonsticky,
8c1a1077
PJ
2290 doc: /* Alist of properties vs the corresponding non-stickinesses.
2291Each element has the form (PROPERTY . NONSTICKINESS).
2292
2293If a character in a buffer has PROPERTY, new text inserted adjacent to
2294the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2295inherits it if NONSTICKINESS is nil. The front-sticky and
2296rear-nonsticky properties of the character overrides NONSTICKINESS. */);
98ebf860
SM
2297 /* Text property `syntax-table' should be nonsticky by default. */
2298 Vtext_property_default_nonsticky
2299 = Fcons (Fcons (intern ("syntax-table"), Qt), Qnil);
abc2f676 2300
318d2fa8
RS
2301 staticpro (&interval_insert_behind_hooks);
2302 staticpro (&interval_insert_in_front_hooks);
2303 interval_insert_behind_hooks = Qnil;
2304 interval_insert_in_front_hooks = Qnil;
2305
81f6e55f 2306
d418ef42
JA
2307 /* Common attributes one might give text */
2308
2309 staticpro (&Qforeground);
2310 Qforeground = intern ("foreground");
2311 staticpro (&Qbackground);
2312 Qbackground = intern ("background");
2313 staticpro (&Qfont);
2314 Qfont = intern ("font");
2315 staticpro (&Qstipple);
2316 Qstipple = intern ("stipple");
2317 staticpro (&Qunderline);
2318 Qunderline = intern ("underline");
2319 staticpro (&Qread_only);
2320 Qread_only = intern ("read-only");
2321 staticpro (&Qinvisible);
2322 Qinvisible = intern ("invisible");
46b4e741
KH
2323 staticpro (&Qintangible);
2324 Qintangible = intern ("intangible");
dc70cea7
RS
2325 staticpro (&Qcategory);
2326 Qcategory = intern ("category");
2327 staticpro (&Qlocal_map);
2328 Qlocal_map = intern ("local-map");
19e1c426
RS
2329 staticpro (&Qfront_sticky);
2330 Qfront_sticky = intern ("front-sticky");
2331 staticpro (&Qrear_nonsticky);
2332 Qrear_nonsticky = intern ("rear-nonsticky");
69bb837e
RS
2333 staticpro (&Qmouse_face);
2334 Qmouse_face = intern ("mouse-face");
d418ef42
JA
2335
2336 /* Properties that text might use to specify certain actions */
2337
2338 staticpro (&Qmouse_left);
2339 Qmouse_left = intern ("mouse-left");
2340 staticpro (&Qmouse_entered);
2341 Qmouse_entered = intern ("mouse-entered");
2342 staticpro (&Qpoint_left);
2343 Qpoint_left = intern ("point-left");
2344 staticpro (&Qpoint_entered);
2345 Qpoint_entered = intern ("point-entered");
d418ef42
JA
2346
2347 defsubr (&Stext_properties_at);
5fbe2a44 2348 defsubr (&Sget_text_property);
eb769fd7 2349 defsubr (&Sget_char_property);
97a1bc63 2350 defsubr (&Sget_char_property_and_overlay);
fcab51aa
RS
2351 defsubr (&Snext_char_property_change);
2352 defsubr (&Sprevious_char_property_change);
b7e047fb
MB
2353 defsubr (&Snext_single_char_property_change);
2354 defsubr (&Sprevious_single_char_property_change);
d418ef42 2355 defsubr (&Snext_property_change);
9c79dd1b 2356 defsubr (&Snext_single_property_change);
d418ef42 2357 defsubr (&Sprevious_property_change);
9c79dd1b 2358 defsubr (&Sprevious_single_property_change);
d418ef42 2359 defsubr (&Sadd_text_properties);
d4b530ad 2360 defsubr (&Sput_text_property);
d418ef42
JA
2361 defsubr (&Sset_text_properties);
2362 defsubr (&Sremove_text_properties);
11713b6d 2363 defsubr (&Sremove_list_of_text_properties);
ad9c1940
JB
2364 defsubr (&Stext_property_any);
2365 defsubr (&Stext_property_not_all);
5fbe2a44 2366/* defsubr (&Serase_text_properties); */
15e4954b 2367/* defsubr (&Scopy_text_properties); */
d418ef42 2368}
ab5796a9
MB
2369
2370/* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2371 (do not change this comment) */