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