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