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