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