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