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