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