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