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