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