(keyboard.o): Depend on syntax.h.
[bpt/emacs.git] / src / textprop.c
CommitLineData
d418ef42 1/* Interface code for dealing with text properties.
cdf3e5a2 2 Copyright (C) 1993, 1994, 1995, 1997 Free Software Foundation, Inc.
d418ef42
JA
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
71dfa9f4 8the Free Software Foundation; either version 2, or (at your option)
d418ef42
JA
9any later version.
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
17along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
d418ef42 20
18160b98 21#include <config.h>
d418ef42
JA
22#include "lisp.h"
23#include "intervals.h"
24#include "buffer.h"
f5957179 25#include "window.h"
59a486ab
RS
26
27#ifndef NULL
28#define NULL (void *)0
29#endif
318d2fa8
RS
30
31/* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
33
34#define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
d418ef42
JA
35\f
36
37/* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
40
9c79dd1b
JA
41 set_properties needs to deal with the interval property cache.
42
d418ef42 43 It is assumed that for any interval plist, a property appears
d4b530ad 44 only once on the list. Although some code i.e., remove_properties,
d418ef42 45 handles the more general case, the uniqueness of properties is
eb8c3be9 46 necessary for the system to remain consistent. This requirement
cdf3e5a2 47 is enforced by the subrs installing properties onto the intervals. */
d418ef42 48
25013c26
JA
49/* The rest of the file is within this conditional */
50#ifdef USE_TEXT_PROPERTIES
d418ef42 51\f
cdf3e5a2 52/* Types of hooks. */
d418ef42
JA
53Lisp_Object Qmouse_left;
54Lisp_Object Qmouse_entered;
55Lisp_Object Qpoint_left;
56Lisp_Object Qpoint_entered;
dc70cea7
RS
57Lisp_Object Qcategory;
58Lisp_Object Qlocal_map;
d418ef42 59
cdf3e5a2 60/* Visual properties text (including strings) may have. */
d418ef42 61Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
46b4e741 62Lisp_Object Qinvisible, Qread_only, Qintangible;
19e1c426
RS
63
64/* Sticky properties */
65Lisp_Object Qfront_sticky, Qrear_nonsticky;
d7b4e137
JB
66
67/* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
68 the o1's cdr. Otherwise, return zero. This is handy for
69 traversing plists. */
8c8c10fe 70#define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCONS (o1)->cdr, CONSP (o2)))
d7b4e137 71
688a5a0f 72Lisp_Object Vinhibit_point_motion_hooks;
ad1b2f20 73Lisp_Object Vdefault_text_properties;
688a5a0f 74
318d2fa8
RS
75/* verify_interval_modification saves insertion hooks here
76 to be run later by report_interval_modification. */
77Lisp_Object interval_insert_behind_hooks;
78Lisp_Object interval_insert_in_front_hooks;
d418ef42 79\f
ac876a79
JA
80/* Extract the interval at the position pointed to by BEGIN from
81 OBJECT, a string or buffer. Additionally, check that the positions
82 pointed to by BEGIN and END are within the bounds of OBJECT, and
83 reverse them if *BEGIN is greater than *END. The objects pointed
84 to by BEGIN and END may be integers or markers; if the latter, they
85 are coerced to integers.
d418ef42 86
d4b530ad
RS
87 When OBJECT is a string, we increment *BEGIN and *END
88 to make them origin-one.
89
d418ef42
JA
90 Note that buffer points don't correspond to interval indices.
91 For example, point-max is 1 greater than the index of the last
92 character. This difference is handled in the caller, which uses
93 the validated points to determine a length, and operates on that.
94 Exceptions are Ftext_properties_at, Fnext_property_change, and
95 Fprevious_property_change which call this function with BEGIN == END.
96 Handle this case specially.
97
98 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
ac876a79
JA
99 create an interval tree for OBJECT if one doesn't exist, provided
100 the object actually contains text. In the current design, if there
d4b530ad 101 is no text, there can be no text properties. */
d418ef42
JA
102
103#define soft 0
104#define hard 1
105
106static INTERVAL
107validate_interval_range (object, begin, end, force)
108 Lisp_Object object, *begin, *end;
109 int force;
110{
111 register INTERVAL i;
d4b530ad
RS
112 int searchpos;
113
d418ef42
JA
114 CHECK_STRING_OR_BUFFER (object, 0);
115 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
116 CHECK_NUMBER_COERCE_MARKER (*end, 0);
117
118 /* If we are asked for a point, but from a subr which operates
cdf3e5a2 119 on a range, then return nothing. */
64a49ca7 120 if (EQ (*begin, *end) && begin != end)
d418ef42
JA
121 return NULL_INTERVAL;
122
123 if (XINT (*begin) > XINT (*end))
124 {
d4b530ad
RS
125 Lisp_Object n;
126 n = *begin;
d418ef42 127 *begin = *end;
d4b530ad 128 *end = n;
d418ef42
JA
129 }
130
5d2fa46f 131 if (BUFFERP (object))
d418ef42
JA
132 {
133 register struct buffer *b = XBUFFER (object);
134
d418ef42
JA
135 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
136 && XINT (*end) <= BUF_ZV (b)))
137 args_out_of_range (*begin, *end);
866bf246 138 i = BUF_INTERVALS (b);
d418ef42 139
cdf3e5a2 140 /* If there's no text, there are no properties. */
d4b530ad
RS
141 if (BUF_BEGV (b) == BUF_ZV (b))
142 return NULL_INTERVAL;
143
144 searchpos = XINT (*begin);
d418ef42
JA
145 }
146 else
147 {
148 register struct Lisp_String *s = XSTRING (object);
149
d4b530ad 150 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
d418ef42
JA
151 && XINT (*end) <= s->size))
152 args_out_of_range (*begin, *end);
d4b530ad
RS
153 /* User-level Positions in strings start with 0,
154 but the interval code always wants positions starting with 1. */
330e7312 155 XSETFASTINT (*begin, XFASTINT (*begin) + 1);
b1e94638 156 if (begin != end)
330e7312 157 XSETFASTINT (*end, XFASTINT (*end) + 1);
d418ef42 158 i = s->intervals;
d4b530ad
RS
159
160 if (s->size == 0)
161 return NULL_INTERVAL;
162
163 searchpos = XINT (*begin);
d418ef42
JA
164 }
165
166 if (NULL_INTERVAL_P (i))
167 return (force ? create_root_interval (object) : i);
168
d4b530ad 169 return find_interval (i, searchpos);
d418ef42
JA
170}
171
172/* Validate LIST as a property list. If LIST is not a list, then
173 make one consisting of (LIST nil). Otherwise, verify that LIST
cdf3e5a2 174 is even numbered and thus suitable as a plist. */
d418ef42
JA
175
176static Lisp_Object
177validate_plist (list)
4d780c76 178 Lisp_Object list;
d418ef42
JA
179{
180 if (NILP (list))
181 return Qnil;
182
183 if (CONSP (list))
184 {
185 register int i;
186 register Lisp_Object tail;
187 for (i = 0, tail = list; !NILP (tail); i++)
b1e94638
JB
188 {
189 tail = Fcdr (tail);
190 QUIT;
191 }
d418ef42
JA
192 if (i & 1)
193 error ("Odd length text property list");
194 return list;
195 }
196
197 return Fcons (list, Fcons (Qnil, Qnil));
198}
199
d418ef42 200/* Return nonzero if interval I has all the properties,
cdf3e5a2 201 with the same values, of list PLIST. */
d418ef42
JA
202
203static int
204interval_has_all_properties (plist, i)
205 Lisp_Object plist;
206 INTERVAL i;
207{
208 register Lisp_Object tail1, tail2, sym1, sym2;
209 register int found;
210
cdf3e5a2 211 /* Go through each element of PLIST. */
d418ef42
JA
212 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
213 {
214 sym1 = Fcar (tail1);
215 found = 0;
216
217 /* Go through I's plist, looking for sym1 */
218 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
219 if (EQ (sym1, Fcar (tail2)))
220 {
221 /* Found the same property on both lists. If the
cdf3e5a2 222 values are unequal, return zero. */
734c51b2 223 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
d418ef42
JA
224 return 0;
225
cdf3e5a2 226 /* Property has same value on both lists; go to next one. */
d418ef42
JA
227 found = 1;
228 break;
229 }
230
231 if (! found)
232 return 0;
233 }
234
235 return 1;
236}
237
238/* Return nonzero if the plist of interval I has any of the
cdf3e5a2 239 properties of PLIST, regardless of their values. */
d418ef42
JA
240
241static INLINE int
242interval_has_some_properties (plist, i)
243 Lisp_Object plist;
244 INTERVAL i;
245{
246 register Lisp_Object tail1, tail2, sym;
247
cdf3e5a2 248 /* Go through each element of PLIST. */
d418ef42
JA
249 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
250 {
251 sym = Fcar (tail1);
252
253 /* Go through i's plist, looking for tail1 */
254 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
255 if (EQ (sym, Fcar (tail2)))
256 return 1;
257 }
258
259 return 0;
260}
d4b530ad 261\f
d7b4e137
JB
262/* Changing the plists of individual intervals. */
263
264/* Return the value of PROP in property-list PLIST, or Qunbound if it
265 has none. */
64a49ca7 266static Lisp_Object
d7b4e137 267property_value (plist, prop)
33ca3504 268 Lisp_Object plist, prop;
d7b4e137
JB
269{
270 Lisp_Object value;
271
272 while (PLIST_ELT_P (plist, value))
273 if (EQ (XCONS (plist)->car, prop))
274 return XCONS (value)->car;
275 else
276 plist = XCONS (value)->cdr;
277
278 return Qunbound;
279}
280
d4b530ad
RS
281/* Set the properties of INTERVAL to PROPERTIES,
282 and record undo info for the previous values.
283 OBJECT is the string or buffer that INTERVAL belongs to. */
284
285static void
286set_properties (properties, interval, object)
287 Lisp_Object properties, object;
288 INTERVAL interval;
289{
d7b4e137 290 Lisp_Object sym, value;
d4b530ad 291
d7b4e137 292 if (BUFFERP (object))
d4b530ad 293 {
d7b4e137
JB
294 /* For each property in the old plist which is missing from PROPERTIES,
295 or has a different value in PROPERTIES, make an undo record. */
296 for (sym = interval->plist;
297 PLIST_ELT_P (sym, value);
298 sym = XCONS (value)->cdr)
299 if (! EQ (property_value (properties, XCONS (sym)->car),
300 XCONS (value)->car))
f7a9275a 301 {
f7a9275a
RS
302 record_property_change (interval->position, LENGTH (interval),
303 XCONS (sym)->car, XCONS (value)->car,
304 object);
305 }
d7b4e137
JB
306
307 /* For each new property that has no value at all in the old plist,
308 make an undo record binding it to nil, so it will be removed. */
309 for (sym = properties;
310 PLIST_ELT_P (sym, value);
311 sym = XCONS (value)->cdr)
312 if (EQ (property_value (interval->plist, XCONS (sym)->car), Qunbound))
f7a9275a 313 {
f7a9275a
RS
314 record_property_change (interval->position, LENGTH (interval),
315 XCONS (sym)->car, Qnil,
316 object);
317 }
d4b530ad
RS
318 }
319
320 /* Store new properties. */
321 interval->plist = Fcopy_sequence (properties);
322}
d418ef42
JA
323
324/* Add the properties of PLIST to the interval I, or set
325 the value of I's property to the value of the property on PLIST
326 if they are different.
327
d4b530ad
RS
328 OBJECT should be the string or buffer the interval is in.
329
d418ef42
JA
330 Return nonzero if this changes I (i.e., if any members of PLIST
331 are actually added to I's plist) */
332
d4b530ad
RS
333static int
334add_properties (plist, i, object)
d418ef42
JA
335 Lisp_Object plist;
336 INTERVAL i;
d4b530ad 337 Lisp_Object object;
d418ef42 338{
c98da214 339 Lisp_Object tail1, tail2, sym1, val1;
d418ef42
JA
340 register int changed = 0;
341 register int found;
c98da214
RS
342 struct gcpro gcpro1, gcpro2, gcpro3;
343
344 tail1 = plist;
345 sym1 = Qnil;
346 val1 = Qnil;
347 /* No need to protect OBJECT, because we can GC only in the case
348 where it is a buffer, and live buffers are always protected.
349 I and its plist are also protected, via OBJECT. */
350 GCPRO3 (tail1, sym1, val1);
d418ef42 351
cdf3e5a2 352 /* Go through each element of PLIST. */
d418ef42
JA
353 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
354 {
355 sym1 = Fcar (tail1);
356 val1 = Fcar (Fcdr (tail1));
357 found = 0;
358
359 /* Go through I's plist, looking for sym1 */
360 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
361 if (EQ (sym1, Fcar (tail2)))
362 {
c98da214
RS
363 /* No need to gcpro, because tail2 protects this
364 and it must be a cons cell (we get an error otherwise). */
3814ccf5 365 register Lisp_Object this_cdr;
d418ef42 366
3814ccf5 367 this_cdr = Fcdr (tail2);
cdf3e5a2 368 /* Found the property. Now check its value. */
d418ef42
JA
369 found = 1;
370
371 /* The properties have the same value on both lists.
cdf3e5a2 372 Continue to the next property. */
734c51b2 373 if (EQ (val1, Fcar (this_cdr)))
d418ef42
JA
374 break;
375
d4b530ad 376 /* Record this change in the buffer, for undo purposes. */
5d2fa46f 377 if (BUFFERP (object))
d4b530ad 378 {
f7a9275a
RS
379 record_property_change (i->position, LENGTH (i),
380 sym1, Fcar (this_cdr), object);
d4b530ad
RS
381 }
382
d418ef42
JA
383 /* I's property has a different value -- change it */
384 Fsetcar (this_cdr, val1);
385 changed++;
386 break;
387 }
388
389 if (! found)
390 {
d4b530ad 391 /* Record this change in the buffer, for undo purposes. */
5d2fa46f 392 if (BUFFERP (object))
d4b530ad 393 {
f7a9275a
RS
394 record_property_change (i->position, LENGTH (i),
395 sym1, Qnil, object);
d4b530ad 396 }
d418ef42
JA
397 i->plist = Fcons (sym1, Fcons (val1, i->plist));
398 changed++;
399 }
400 }
401
c98da214
RS
402 UNGCPRO;
403
d418ef42
JA
404 return changed;
405}
406
407/* For any members of PLIST which are properties of I, remove them
d4b530ad
RS
408 from I's plist.
409 OBJECT is the string or buffer containing I. */
d418ef42 410
d4b530ad
RS
411static int
412remove_properties (plist, i, object)
d418ef42
JA
413 Lisp_Object plist;
414 INTERVAL i;
d4b530ad 415 Lisp_Object object;
d418ef42 416{
3814ccf5 417 register Lisp_Object tail1, tail2, sym, current_plist;
d418ef42
JA
418 register int changed = 0;
419
3814ccf5 420 current_plist = i->plist;
cdf3e5a2 421 /* Go through each element of plist. */
d418ef42
JA
422 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
423 {
424 sym = Fcar (tail1);
425
426 /* First, remove the symbol if its at the head of the list */
427 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
428 {
5d2fa46f 429 if (BUFFERP (object))
d4b530ad 430 {
f7a9275a
RS
431 record_property_change (i->position, LENGTH (i),
432 sym, Fcar (Fcdr (current_plist)),
433 object);
d4b530ad
RS
434 }
435
d418ef42
JA
436 current_plist = Fcdr (Fcdr (current_plist));
437 changed++;
438 }
439
440 /* Go through i's plist, looking for sym */
441 tail2 = current_plist;
442 while (! NILP (tail2))
443 {
3814ccf5
KH
444 register Lisp_Object this;
445 this = Fcdr (Fcdr (tail2));
d418ef42
JA
446 if (EQ (sym, Fcar (this)))
447 {
5d2fa46f 448 if (BUFFERP (object))
d4b530ad 449 {
f7a9275a
RS
450 record_property_change (i->position, LENGTH (i),
451 sym, Fcar (Fcdr (this)), object);
d4b530ad
RS
452 }
453
d418ef42
JA
454 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
455 changed++;
456 }
457 tail2 = this;
458 }
459 }
460
461 if (changed)
462 i->plist = current_plist;
463 return changed;
464}
465
d4b530ad 466#if 0
d418ef42 467/* Remove all properties from interval I. Return non-zero
cdf3e5a2 468 if this changes the interval. */
d418ef42
JA
469
470static INLINE int
471erase_properties (i)
472 INTERVAL i;
473{
474 if (NILP (i->plist))
475 return 0;
476
477 i->plist = Qnil;
478 return 1;
479}
d4b530ad 480#endif
d418ef42 481\f
cdf3e5a2
RS
482/* Returns the interval of the POSITION in OBJECT.
483 POSITION is BEG-based. */
484
485INTERVAL
486interval_of (position, object)
487 int position;
488 Lisp_Object object;
489{
490 register INTERVAL i;
491 int beg, end;
492
493 if (NILP (object))
494 XSETBUFFER (object, current_buffer);
d0cb872a
KH
495 else if (EQ (object, Qt))
496 return NULL_INTERVAL;
cdf3e5a2
RS
497
498 CHECK_STRING_OR_BUFFER (object, 0);
499
500 if (BUFFERP (object))
501 {
502 register struct buffer *b = XBUFFER (object);
503
504 beg = BUF_BEGV (b);
505 end = BUF_ZV (b);
506 i = BUF_INTERVALS (b);
507 }
508 else
509 {
510 register struct Lisp_String *s = XSTRING (object);
511
512 /* We expect position to be 1-based. */
513 beg = BEG;
514 end = s->size + BEG;
515 i = s->intervals;
516 }
517
518 if (!(beg <= position && position <= end))
c7ef8e24 519 args_out_of_range (make_number (position), make_number (position));
cdf3e5a2
RS
520 if (beg == end || NULL_INTERVAL_P (i))
521 return NULL_INTERVAL;
522
523 return find_interval (i, position);
524}
525\f
d418ef42
JA
526DEFUN ("text-properties-at", Ftext_properties_at,
527 Stext_properties_at, 1, 2, 0,
96f90544
RS
528 "Return the list of properties of the character at POSITION in OBJECT.\n\
529OBJECT is the string or buffer to look for the properties in;\n\
530nil means the current buffer.\n\
d4b530ad 531If POSITION is at the end of OBJECT, the value is nil.")
1f5e848a
EN
532 (position, object)
533 Lisp_Object position, object;
d418ef42
JA
534{
535 register INTERVAL i;
d418ef42
JA
536
537 if (NILP (object))
c8a4fc3d 538 XSETBUFFER (object, current_buffer);
d418ef42 539
1f5e848a 540 i = validate_interval_range (object, &position, &position, soft);
d418ef42
JA
541 if (NULL_INTERVAL_P (i))
542 return Qnil;
1f5e848a 543 /* If POSITION is at the end of the interval,
d4b530ad
RS
544 it means it's the end of OBJECT.
545 There are no properties at the very end,
546 since no character follows. */
1f5e848a 547 if (XINT (position) == LENGTH (i) + i->position)
d4b530ad 548 return Qnil;
d418ef42
JA
549
550 return i->plist;
551}
552
5fbe2a44 553DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
1f5e848a 554 "Return the value of POSITION's property PROP, in OBJECT.\n\
d4b530ad
RS
555OBJECT is optional and defaults to the current buffer.\n\
556If POSITION is at the end of OBJECT, the value is nil.")
1f5e848a
EN
557 (position, prop, object)
558 Lisp_Object position, object;
46bb7c2b 559 Lisp_Object prop;
5fbe2a44 560{
1f5e848a 561 return textget (Ftext_properties_at (position, object), prop);
5fbe2a44
RS
562}
563
f5957179 564DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
1f5e848a 565 "Return the value of POSITION's property PROP, in OBJECT.\n\
f5957179 566OBJECT is optional and defaults to the current buffer.\n\
1f5e848a 567If POSITION is at the end of OBJECT, the value is nil.\n\
f5957179 568If OBJECT is a buffer, then overlay properties are considered as well as\n\
99830d63
KH
569text properties.\n\
570If OBJECT is a window, then that window's buffer is used, but window-specific\n\
f5957179 571overlays are considered only if they are associated with OBJECT.")
1f5e848a
EN
572 (position, prop, object)
573 Lisp_Object position, object;
f5957179
KH
574 register Lisp_Object prop;
575{
576 struct window *w = 0;
577
1f5e848a 578 CHECK_NUMBER_COERCE_MARKER (position, 0);
f5957179
KH
579
580 if (NILP (object))
c8a4fc3d 581 XSETBUFFER (object, current_buffer);
f5957179
KH
582
583 if (WINDOWP (object))
584 {
585 w = XWINDOW (object);
64a49ca7 586 object = w->buffer;
f5957179
KH
587 }
588 if (BUFFERP (object))
589 {
1f5e848a 590 int posn = XINT (position);
f5957179
KH
591 int noverlays;
592 Lisp_Object *overlay_vec, tem;
593 int next_overlay;
594 int len;
cbc55f55
RS
595 struct buffer *obuf = current_buffer;
596
597 set_buffer_temp (XBUFFER (object));
f5957179
KH
598
599 /* First try with room for 40 overlays. */
600 len = 40;
601 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
602
59a486ab
RS
603 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
604 &next_overlay, NULL);
f5957179
KH
605
606 /* If there are more than 40,
607 make enough space for all, and try again. */
608 if (noverlays > len)
609 {
610 len = noverlays;
611 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
59a486ab
RS
612 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
613 &next_overlay, NULL);
f5957179
KH
614 }
615 noverlays = sort_overlays (overlay_vec, noverlays, w);
616
cbc55f55
RS
617 set_buffer_temp (obuf);
618
f5957179
KH
619 /* Now check the overlays in order of decreasing priority. */
620 while (--noverlays >= 0)
621 {
622 tem = Foverlay_get (overlay_vec[noverlays], prop);
623 if (!NILP (tem))
624 return (tem);
625 }
626 }
627 /* Not a buffer, or no appropriate overlay, so fall through to the
628 simpler case. */
1f5e848a 629 return (Fget_text_property (position, prop, object));
f5957179 630}
fcab51aa
RS
631\f
632DEFUN ("next-char-property-change", Fnext_char_property_change,
633 Snext_char_property_change, 1, 2, 0,
634 "Return the position of next text property or overlay change.\n\
635This scans characters forward from POSITION in OBJECT till it finds\n\
636a change in some text property, or the beginning or end of an overlay,\n\
637and returns the position of that.\n\
638If none is found, the function returns (point-max).\n\
639\n\
640If the optional third argument LIMIT is non-nil, don't search\n\
641past position LIMIT; return LIMIT if nothing is found before LIMIT.")
642 (position, limit)
643 Lisp_Object position, limit;
644{
645 Lisp_Object temp;
646
647 temp = Fnext_overlay_change (position);
648 if (! NILP (limit))
649 {
650 CHECK_NUMBER (limit, 2);
651 if (XINT (limit) < XINT (temp))
652 temp = limit;
653 }
654 return Fnext_property_change (position, Qnil, temp);
655}
656
657DEFUN ("previous-char-property-change", Fprevious_char_property_change,
658 Sprevious_char_property_change, 1, 2, 0,
659 "Return the position of previous text property or overlay change.\n\
660Scans characters backward from POSITION in OBJECT till it finds\n\
661a change in some text property, or the beginning or end of an overlay,\n\
662and returns the position of that.\n\
663If none is found, the function returns (point-max).\n\
664\n\
665If the optional third argument LIMIT is non-nil, don't search\n\
666past position LIMIT; return LIMIT if nothing is found before LIMIT.")
667 (position, limit)
668 Lisp_Object position, limit;
669{
670 Lisp_Object temp;
f5957179 671
fcab51aa
RS
672 temp = Fprevious_overlay_change (position);
673 if (! NILP (limit))
674 {
675 CHECK_NUMBER (limit, 2);
676 if (XINT (limit) > XINT (temp))
677 temp = limit;
678 }
679 return Fprevious_property_change (position, Qnil, temp);
680}
681\f
d418ef42 682DEFUN ("next-property-change", Fnext_property_change,
111b637d 683 Snext_property_change, 1, 3, 0,
5fbe2a44 684 "Return the position of next property change.\n\
1f5e848a 685Scans characters forward from POSITION in OBJECT till it finds\n\
5fbe2a44
RS
686a change in some text property, then returns the position of the change.\n\
687The optional second argument OBJECT is the string or buffer to scan.\n\
688Return nil if the property is constant all the way to the end of OBJECT.\n\
1f5e848a 689If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
111b637d
RS
690If the optional third argument LIMIT is non-nil, don't search\n\
691past position LIMIT; return LIMIT if nothing is found before LIMIT.")
1f5e848a
EN
692 (position, object, limit)
693 Lisp_Object position, object, limit;
d418ef42
JA
694{
695 register INTERVAL i, next;
696
5fbe2a44 697 if (NILP (object))
c8a4fc3d 698 XSETBUFFER (object, current_buffer);
5fbe2a44 699
041aa96f 700 if (! NILP (limit) && ! EQ (limit, Qt))
1387d54e
KH
701 CHECK_NUMBER_COERCE_MARKER (limit, 0);
702
1f5e848a 703 i = validate_interval_range (object, &position, &position, soft);
d418ef42 704
041aa96f
RS
705 /* If LIMIT is t, return start of next interval--don't
706 bother checking further intervals. */
707 if (EQ (limit, Qt))
708 {
44214c1b
RS
709 if (NULL_INTERVAL_P (i))
710 next = i;
711 else
712 next = next_interval (i);
713
c7b6dfa6 714 if (NULL_INTERVAL_P (next))
1f5e848a
EN
715 XSETFASTINT (position, (STRINGP (object)
716 ? XSTRING (object)->size
717 : BUF_ZV (XBUFFER (object))));
c7b6dfa6 718 else
1f5e848a
EN
719 XSETFASTINT (position, next->position - (STRINGP (object)));
720 return position;
041aa96f
RS
721 }
722
44214c1b
RS
723 if (NULL_INTERVAL_P (i))
724 return limit;
725
726 next = next_interval (i);
727
111b637d
RS
728 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
729 && (NILP (limit) || next->position < XFASTINT (limit)))
d418ef42
JA
730 next = next_interval (next);
731
732 if (NULL_INTERVAL_P (next))
111b637d
RS
733 return limit;
734 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
735 return limit;
d418ef42 736
1f5e848a
EN
737 XSETFASTINT (position, next->position - (STRINGP (object)));
738 return position;
19e1c426
RS
739}
740
741/* Return 1 if there's a change in some property between BEG and END. */
742
743int
744property_change_between_p (beg, end)
745 int beg, end;
746{
747 register INTERVAL i, next;
748 Lisp_Object object, pos;
749
c8a4fc3d 750 XSETBUFFER (object, current_buffer);
e9c4fbcd 751 XSETFASTINT (pos, beg);
19e1c426
RS
752
753 i = validate_interval_range (object, &pos, &pos, soft);
754 if (NULL_INTERVAL_P (i))
755 return 0;
756
757 next = next_interval (i);
758 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
759 {
760 next = next_interval (next);
e050ef74
RS
761 if (NULL_INTERVAL_P (next))
762 return 0;
19e1c426
RS
763 if (next->position >= end)
764 return 0;
765 }
766
767 if (NULL_INTERVAL_P (next))
768 return 0;
769
770 return 1;
d418ef42
JA
771}
772
9c79dd1b 773DEFUN ("next-single-property-change", Fnext_single_property_change,
111b637d 774 Snext_single_property_change, 2, 4, 0,
5fbe2a44 775 "Return the position of next property change for a specific property.\n\
1f5e848a 776Scans characters forward from POSITION till it finds\n\
5fbe2a44
RS
777a change in the PROP property, then returns the position of the change.\n\
778The optional third argument OBJECT is the string or buffer to scan.\n\
da625a3c 779The property values are compared with `eq'.\n\
5fbe2a44 780Return nil if the property is constant all the way to the end of OBJECT.\n\
1f5e848a 781If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
111b637d 782If the optional fourth argument LIMIT is non-nil, don't search\n\
5abb9556 783past position LIMIT; return LIMIT if nothing is found before LIMIT.")
1f5e848a
EN
784 (position, prop, object, limit)
785 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
786{
787 register INTERVAL i, next;
788 register Lisp_Object here_val;
789
5fbe2a44 790 if (NILP (object))
c8a4fc3d 791 XSETBUFFER (object, current_buffer);
5fbe2a44 792
1387d54e
KH
793 if (!NILP (limit))
794 CHECK_NUMBER_COERCE_MARKER (limit, 0);
795
1f5e848a 796 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 797 if (NULL_INTERVAL_P (i))
111b637d 798 return limit;
9c79dd1b 799
6a0486dd 800 here_val = textget (i->plist, prop);
9c79dd1b 801 next = next_interval (i);
6a0486dd 802 while (! NULL_INTERVAL_P (next)
111b637d
RS
803 && EQ (here_val, textget (next->plist, prop))
804 && (NILP (limit) || next->position < XFASTINT (limit)))
9c79dd1b
JA
805 next = next_interval (next);
806
807 if (NULL_INTERVAL_P (next))
111b637d
RS
808 return limit;
809 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
810 return limit;
9c79dd1b 811
1f5e848a
EN
812 XSETFASTINT (position, next->position - (STRINGP (object)));
813 return position;
9c79dd1b
JA
814}
815
d418ef42 816DEFUN ("previous-property-change", Fprevious_property_change,
111b637d 817 Sprevious_property_change, 1, 3, 0,
5fbe2a44 818 "Return the position of previous property change.\n\
1f5e848a 819Scans characters backwards from POSITION in OBJECT till it finds\n\
5fbe2a44
RS
820a change in some text property, then returns the position of the change.\n\
821The optional second argument OBJECT is the string or buffer to scan.\n\
822Return nil if the property is constant all the way to the start of OBJECT.\n\
1f5e848a 823If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
111b637d 824If the optional third argument LIMIT is non-nil, don't search\n\
5abb9556 825back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1f5e848a
EN
826 (position, object, limit)
827 Lisp_Object position, object, limit;
d418ef42
JA
828{
829 register INTERVAL i, previous;
830
5fbe2a44 831 if (NILP (object))
c8a4fc3d 832 XSETBUFFER (object, current_buffer);
5fbe2a44 833
1387d54e
KH
834 if (!NILP (limit))
835 CHECK_NUMBER_COERCE_MARKER (limit, 0);
836
1f5e848a 837 i = validate_interval_range (object, &position, &position, soft);
d418ef42 838 if (NULL_INTERVAL_P (i))
111b637d 839 return limit;
d418ef42 840
53b7feec 841 /* Start with the interval containing the char before point. */
1f5e848a 842 if (i->position == XFASTINT (position))
53b7feec
RS
843 i = previous_interval (i);
844
d418ef42 845 previous = previous_interval (i);
111b637d
RS
846 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
847 && (NILP (limit)
848 || previous->position + LENGTH (previous) > XFASTINT (limit)))
d418ef42
JA
849 previous = previous_interval (previous);
850 if (NULL_INTERVAL_P (previous))
111b637d
RS
851 return limit;
852 if (!NILP (limit)
853 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
854 return limit;
d418ef42 855
1f5e848a 856 XSETFASTINT (position, (previous->position + LENGTH (previous)
e9c4fbcd 857 - (STRINGP (object))));
1f5e848a 858 return position;
d418ef42
JA
859}
860
9c79dd1b 861DEFUN ("previous-single-property-change", Fprevious_single_property_change,
111b637d 862 Sprevious_single_property_change, 2, 4, 0,
5fbe2a44 863 "Return the position of previous property change for a specific property.\n\
1f5e848a 864Scans characters backward from POSITION till it finds\n\
5fbe2a44
RS
865a change in the PROP property, then returns the position of the change.\n\
866The optional third argument OBJECT is the string or buffer to scan.\n\
93fda178 867The property values are compared with `eq'.\n\
5fbe2a44 868Return nil if the property is constant all the way to the start of OBJECT.\n\
1f5e848a 869If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
111b637d 870If the optional fourth argument LIMIT is non-nil, don't search\n\
5abb9556 871back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1f5e848a
EN
872 (position, prop, object, limit)
873 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
874{
875 register INTERVAL i, previous;
876 register Lisp_Object here_val;
877
5fbe2a44 878 if (NILP (object))
c8a4fc3d 879 XSETBUFFER (object, current_buffer);
5fbe2a44 880
1387d54e
KH
881 if (!NILP (limit))
882 CHECK_NUMBER_COERCE_MARKER (limit, 0);
883
1f5e848a 884 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 885
53b7feec 886 /* Start with the interval containing the char before point. */
1f5e848a 887 if (! NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
53b7feec
RS
888 i = previous_interval (i);
889
6873cfa3
KH
890 if (NULL_INTERVAL_P (i))
891 return limit;
892
6a0486dd 893 here_val = textget (i->plist, prop);
9c79dd1b
JA
894 previous = previous_interval (i);
895 while (! NULL_INTERVAL_P (previous)
111b637d
RS
896 && EQ (here_val, textget (previous->plist, prop))
897 && (NILP (limit)
898 || previous->position + LENGTH (previous) > XFASTINT (limit)))
9c79dd1b
JA
899 previous = previous_interval (previous);
900 if (NULL_INTERVAL_P (previous))
111b637d
RS
901 return limit;
902 if (!NILP (limit)
903 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
904 return limit;
9c79dd1b 905
1f5e848a 906 XSETFASTINT (position, (previous->position + LENGTH (previous)
e9c4fbcd 907 - (STRINGP (object))));
1f5e848a 908 return position;
9c79dd1b 909}
fcab51aa 910\f
c98da214
RS
911/* Callers note, this can GC when OBJECT is a buffer (or nil). */
912
d418ef42 913DEFUN ("add-text-properties", Fadd_text_properties,
5fbe2a44
RS
914 Sadd_text_properties, 3, 4, 0,
915 "Add properties to the text from START to END.\n\
1f5e848a 916The third argument PROPERTIES is a property list\n\
5fbe2a44
RS
917specifying the property values to add.\n\
918The optional fourth argument, OBJECT,\n\
919is the string or buffer containing the text.\n\
920Return t if any property value actually changed, nil otherwise.")
921 (start, end, properties, object)
922 Lisp_Object start, end, properties, object;
d418ef42
JA
923{
924 register INTERVAL i, unchanged;
caa31568 925 register int s, len, modified = 0;
c98da214 926 struct gcpro gcpro1;
d418ef42
JA
927
928 properties = validate_plist (properties);
929 if (NILP (properties))
930 return Qnil;
931
5fbe2a44 932 if (NILP (object))
c8a4fc3d 933 XSETBUFFER (object, current_buffer);
5fbe2a44 934
d418ef42
JA
935 i = validate_interval_range (object, &start, &end, hard);
936 if (NULL_INTERVAL_P (i))
937 return Qnil;
938
939 s = XINT (start);
940 len = XINT (end) - s;
941
c98da214
RS
942 /* No need to protect OBJECT, because we GC only if it's a buffer,
943 and live buffers are always protected. */
944 GCPRO1 (properties);
945
d418ef42 946 /* If we're not starting on an interval boundary, we have to
cdf3e5a2 947 split this interval. */
d418ef42
JA
948 if (i->position != s)
949 {
950 /* If this interval already has the properties, we can
cdf3e5a2 951 skip it. */
d418ef42
JA
952 if (interval_has_all_properties (properties, i))
953 {
954 int got = (LENGTH (i) - (s - i->position));
955 if (got >= len)
64db1307 956 RETURN_UNGCPRO (Qnil);
d418ef42 957 len -= got;
05d5b93e 958 i = next_interval (i);
d418ef42
JA
959 }
960 else
961 {
962 unchanged = i;
ad9c1940 963 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 964 copy_properties (unchanged, i);
d418ef42
JA
965 }
966 }
967
2a631db1
RS
968 if (BUFFERP (object))
969 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 970
daa5e28f 971 /* We are at the beginning of interval I, with LEN chars to scan. */
caa31568 972 for (;;)
d418ef42 973 {
d4b530ad
RS
974 if (i == 0)
975 abort ();
976
d418ef42
JA
977 if (LENGTH (i) >= len)
978 {
c98da214
RS
979 /* We can UNGCPRO safely here, because there will be just
980 one more chance to gc, in the next call to add_properties,
981 and after that we will not need PROPERTIES or OBJECT again. */
982 UNGCPRO;
983
d418ef42 984 if (interval_has_all_properties (properties, i))
26c76ace 985 {
2a631db1
RS
986 if (BUFFERP (object))
987 signal_after_change (XINT (start), XINT (end) - XINT (start),
988 XINT (end) - XINT (start));
26c76ace
RS
989
990 return modified ? Qt : Qnil;
991 }
d418ef42
JA
992
993 if (LENGTH (i) == len)
994 {
d4b530ad 995 add_properties (properties, i, object);
2a631db1
RS
996 if (BUFFERP (object))
997 signal_after_change (XINT (start), XINT (end) - XINT (start),
998 XINT (end) - XINT (start));
d418ef42
JA
999 return Qt;
1000 }
1001
1002 /* i doesn't have the properties, and goes past the change limit */
1003 unchanged = i;
ad9c1940 1004 i = split_interval_left (unchanged, len);
d418ef42 1005 copy_properties (unchanged, i);
d4b530ad 1006 add_properties (properties, i, object);
2a631db1
RS
1007 if (BUFFERP (object))
1008 signal_after_change (XINT (start), XINT (end) - XINT (start),
1009 XINT (end) - XINT (start));
d418ef42
JA
1010 return Qt;
1011 }
1012
1013 len -= LENGTH (i);
d4b530ad 1014 modified += add_properties (properties, i, object);
d418ef42
JA
1015 i = next_interval (i);
1016 }
1017}
1018
c98da214
RS
1019/* Callers note, this can GC when OBJECT is a buffer (or nil). */
1020
d4b530ad
RS
1021DEFUN ("put-text-property", Fput_text_property,
1022 Sput_text_property, 4, 5, 0,
1023 "Set one property of the text from START to END.\n\
1f5e848a 1024The third and fourth arguments PROPERTY and VALUE\n\
d4b530ad
RS
1025specify the property to add.\n\
1026The optional fifth argument, OBJECT,\n\
1027is the string or buffer containing the text.")
1f5e848a
EN
1028 (start, end, property, value, object)
1029 Lisp_Object start, end, property, value, object;
d4b530ad
RS
1030{
1031 Fadd_text_properties (start, end,
1f5e848a 1032 Fcons (property, Fcons (value, Qnil)),
d4b530ad
RS
1033 object);
1034 return Qnil;
1035}
1036
d418ef42 1037DEFUN ("set-text-properties", Fset_text_properties,
5fbe2a44
RS
1038 Sset_text_properties, 3, 4, 0,
1039 "Completely replace properties of text from START to END.\n\
1f5e848a 1040The third argument PROPERTIES is the new property list.\n\
5fbe2a44
RS
1041The optional fourth argument, OBJECT,\n\
1042is the string or buffer containing the text.")
1f5e848a
EN
1043 (start, end, properties, object)
1044 Lisp_Object start, end, properties, object;
d418ef42
JA
1045{
1046 register INTERVAL i, unchanged;
9c79dd1b 1047 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42 1048 register int s, len;
33d7d0df 1049 Lisp_Object ostart, oend;
26c76ace 1050 int have_modified = 0;
33d7d0df
RS
1051
1052 ostart = start;
1053 oend = end;
d418ef42 1054
1f5e848a 1055 properties = validate_plist (properties);
d418ef42 1056
5fbe2a44 1057 if (NILP (object))
c8a4fc3d 1058 XSETBUFFER (object, current_buffer);
5fbe2a44 1059
919fa9cb
RS
1060 /* If we want no properties for a whole string,
1061 get rid of its intervals. */
1f5e848a 1062 if (NILP (properties) && STRINGP (object)
919fa9cb
RS
1063 && XFASTINT (start) == 0
1064 && XFASTINT (end) == XSTRING (object)->size)
1065 {
26c76ace
RS
1066 if (! XSTRING (object)->intervals)
1067 return Qt;
1068
919fa9cb
RS
1069 XSTRING (object)->intervals = 0;
1070 return Qt;
1071 }
1072
facc570e 1073 i = validate_interval_range (object, &start, &end, soft);
919fa9cb 1074
d418ef42 1075 if (NULL_INTERVAL_P (i))
facc570e 1076 {
1f5e848a
EN
1077 /* If buffer has no properties, and we want none, return now. */
1078 if (NILP (properties))
facc570e
RS
1079 return Qnil;
1080
33d7d0df
RS
1081 /* Restore the original START and END values
1082 because validate_interval_range increments them for strings. */
1083 start = ostart;
1084 end = oend;
1085
facc570e
RS
1086 i = validate_interval_range (object, &start, &end, hard);
1087 /* This can return if start == end. */
1088 if (NULL_INTERVAL_P (i))
1089 return Qnil;
1090 }
d418ef42
JA
1091
1092 s = XINT (start);
1093 len = XINT (end) - s;
1094
2a631db1
RS
1095 if (BUFFERP (object))
1096 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1097
d418ef42
JA
1098 if (i->position != s)
1099 {
1100 unchanged = i;
ad9c1940 1101 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 1102
d418ef42
JA
1103 if (LENGTH (i) > len)
1104 {
9c79dd1b 1105 copy_properties (unchanged, i);
ad9c1940 1106 i = split_interval_left (i, len);
1f5e848a 1107 set_properties (properties, i, object);
2a631db1
RS
1108 if (BUFFERP (object))
1109 signal_after_change (XINT (start), XINT (end) - XINT (start),
1110 XINT (end) - XINT (start));
26c76ace 1111
d418ef42
JA
1112 return Qt;
1113 }
1114
1f5e848a 1115 set_properties (properties, i, object);
daa5e28f 1116
9c79dd1b 1117 if (LENGTH (i) == len)
26c76ace 1118 {
2a631db1
RS
1119 if (BUFFERP (object))
1120 signal_after_change (XINT (start), XINT (end) - XINT (start),
1121 XINT (end) - XINT (start));
26c76ace
RS
1122
1123 return Qt;
1124 }
9c79dd1b
JA
1125
1126 prev_changed = i;
d418ef42
JA
1127 len -= LENGTH (i);
1128 i = next_interval (i);
1129 }
1130
cd7d971d 1131 /* We are starting at the beginning of an interval, I */
7855e674 1132 while (len > 0)
d418ef42 1133 {
d4b530ad
RS
1134 if (i == 0)
1135 abort ();
1136
d418ef42
JA
1137 if (LENGTH (i) >= len)
1138 {
cd7d971d 1139 if (LENGTH (i) > len)
ad9c1940 1140 i = split_interval_left (i, len);
d418ef42 1141
6f232881
RS
1142 /* We have to call set_properties even if we are going to
1143 merge the intervals, so as to make the undo records
1144 and cause redisplay to happen. */
1f5e848a 1145 set_properties (properties, i, object);
6f232881 1146 if (!NULL_INTERVAL_P (prev_changed))
9c79dd1b 1147 merge_interval_left (i);
2a631db1
RS
1148 if (BUFFERP (object))
1149 signal_after_change (XINT (start), XINT (end) - XINT (start),
1150 XINT (end) - XINT (start));
d418ef42
JA
1151 return Qt;
1152 }
1153
1154 len -= LENGTH (i);
6f232881
RS
1155
1156 /* We have to call set_properties even if we are going to
1157 merge the intervals, so as to make the undo records
1158 and cause redisplay to happen. */
1f5e848a 1159 set_properties (properties, i, object);
9c79dd1b 1160 if (NULL_INTERVAL_P (prev_changed))
6f232881 1161 prev_changed = i;
9c79dd1b
JA
1162 else
1163 prev_changed = i = merge_interval_left (i);
1164
d418ef42
JA
1165 i = next_interval (i);
1166 }
1167
2a631db1
RS
1168 if (BUFFERP (object))
1169 signal_after_change (XINT (start), XINT (end) - XINT (start),
1170 XINT (end) - XINT (start));
d418ef42
JA
1171 return Qt;
1172}
1173
1174DEFUN ("remove-text-properties", Fremove_text_properties,
5fbe2a44
RS
1175 Sremove_text_properties, 3, 4, 0,
1176 "Remove some properties from text from START to END.\n\
1f5e848a 1177The third argument PROPERTIES is a property list\n\
5fbe2a44 1178whose property names specify the properties to remove.\n\
1f5e848a 1179\(The values stored in PROPERTIES are ignored.)\n\
5fbe2a44
RS
1180The optional fourth argument, OBJECT,\n\
1181is the string or buffer containing the text.\n\
1182Return t if any property was actually removed, nil otherwise.")
1f5e848a
EN
1183 (start, end, properties, object)
1184 Lisp_Object start, end, properties, object;
d418ef42
JA
1185{
1186 register INTERVAL i, unchanged;
caa31568 1187 register int s, len, modified = 0;
d418ef42 1188
5fbe2a44 1189 if (NILP (object))
c8a4fc3d 1190 XSETBUFFER (object, current_buffer);
5fbe2a44 1191
d418ef42
JA
1192 i = validate_interval_range (object, &start, &end, soft);
1193 if (NULL_INTERVAL_P (i))
1194 return Qnil;
1195
1196 s = XINT (start);
1197 len = XINT (end) - s;
9c79dd1b 1198
d418ef42
JA
1199 if (i->position != s)
1200 {
1201 /* No properties on this first interval -- return if
cdf3e5a2 1202 it covers the entire region. */
1f5e848a 1203 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1204 {
1205 int got = (LENGTH (i) - (s - i->position));
1206 if (got >= len)
1207 return Qnil;
1208 len -= got;
05d5b93e 1209 i = next_interval (i);
d418ef42 1210 }
daa5e28f
RS
1211 /* Split away the beginning of this interval; what we don't
1212 want to modify. */
d418ef42
JA
1213 else
1214 {
1215 unchanged = i;
ad9c1940 1216 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 1217 copy_properties (unchanged, i);
d418ef42
JA
1218 }
1219 }
1220
2a631db1
RS
1221 if (BUFFERP (object))
1222 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1223
d418ef42 1224 /* We are at the beginning of an interval, with len to scan */
caa31568 1225 for (;;)
d418ef42 1226 {
d4b530ad
RS
1227 if (i == 0)
1228 abort ();
1229
d418ef42
JA
1230 if (LENGTH (i) >= len)
1231 {
1f5e848a 1232 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1233 return modified ? Qt : Qnil;
1234
1235 if (LENGTH (i) == len)
1236 {
1f5e848a 1237 remove_properties (properties, i, object);
2a631db1
RS
1238 if (BUFFERP (object))
1239 signal_after_change (XINT (start), XINT (end) - XINT (start),
1240 XINT (end) - XINT (start));
d418ef42
JA
1241 return Qt;
1242 }
1243
1244 /* i has the properties, and goes past the change limit */
daa5e28f 1245 unchanged = i;
ad9c1940 1246 i = split_interval_left (i, len);
d418ef42 1247 copy_properties (unchanged, i);
1f5e848a 1248 remove_properties (properties, i, object);
2a631db1
RS
1249 if (BUFFERP (object))
1250 signal_after_change (XINT (start), XINT (end) - XINT (start),
1251 XINT (end) - XINT (start));
d418ef42
JA
1252 return Qt;
1253 }
1254
1255 len -= LENGTH (i);
1f5e848a 1256 modified += remove_properties (properties, i, object);
d418ef42
JA
1257 i = next_interval (i);
1258 }
1259}
fcab51aa 1260\f
ad9c1940
JB
1261DEFUN ("text-property-any", Ftext_property_any,
1262 Stext_property_any, 4, 5, 0,
1f5e848a
EN
1263 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1264If so, return the position of the first character whose property PROPERTY\n\
1265is `eq' to VALUE. Otherwise return nil.\n\
ad9c1940
JB
1266The optional fifth argument, OBJECT, is the string or buffer\n\
1267containing the text.")
1f5e848a
EN
1268 (start, end, property, value, object)
1269 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1270{
1271 register INTERVAL i;
1272 register int e, pos;
1273
1274 if (NILP (object))
c8a4fc3d 1275 XSETBUFFER (object, current_buffer);
ad9c1940 1276 i = validate_interval_range (object, &start, &end, soft);
2084fddb
KH
1277 if (NULL_INTERVAL_P (i))
1278 return (!NILP (value) || EQ (start, end) ? Qnil : start);
ad9c1940
JB
1279 e = XINT (end);
1280
1281 while (! NULL_INTERVAL_P (i))
1282 {
1283 if (i->position >= e)
1284 break;
1f5e848a 1285 if (EQ (textget (i->plist, property), value))
ad9c1940
JB
1286 {
1287 pos = i->position;
1288 if (pos < XINT (start))
1289 pos = XINT (start);
5d2fa46f 1290 return make_number (pos - (STRINGP (object)));
ad9c1940
JB
1291 }
1292 i = next_interval (i);
1293 }
1294 return Qnil;
1295}
1296
1297DEFUN ("text-property-not-all", Ftext_property_not_all,
1298 Stext_property_not_all, 4, 5, 0,
1f5e848a
EN
1299 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1300If so, return the position of the first character whose property PROPERTY\n\
1301is not `eq' to VALUE. Otherwise, return nil.\n\
ad9c1940
JB
1302The optional fifth argument, OBJECT, is the string or buffer\n\
1303containing the text.")
1f5e848a
EN
1304 (start, end, property, value, object)
1305 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1306{
1307 register INTERVAL i;
1308 register int s, e;
1309
1310 if (NILP (object))
c8a4fc3d 1311 XSETBUFFER (object, current_buffer);
ad9c1940
JB
1312 i = validate_interval_range (object, &start, &end, soft);
1313 if (NULL_INTERVAL_P (i))
916a3119 1314 return (NILP (value) || EQ (start, end)) ? Qnil : start;
ad9c1940
JB
1315 s = XINT (start);
1316 e = XINT (end);
1317
1318 while (! NULL_INTERVAL_P (i))
1319 {
1320 if (i->position >= e)
1321 break;
1f5e848a 1322 if (! EQ (textget (i->plist, property), value))
ad9c1940
JB
1323 {
1324 if (i->position > s)
1325 s = i->position;
5d2fa46f 1326 return make_number (s - (STRINGP (object)));
ad9c1940
JB
1327 }
1328 i = next_interval (i);
1329 }
1330 return Qnil;
1331}
fcab51aa 1332\f
5fbe2a44
RS
1333#if 0 /* You can use set-text-properties for this. */
1334
d418ef42 1335DEFUN ("erase-text-properties", Ferase_text_properties,
5fbe2a44
RS
1336 Serase_text_properties, 2, 3, 0,
1337 "Remove all properties from the text from START to END.\n\
1338The optional third argument, OBJECT,\n\
1339is the string or buffer containing the text.")
1340 (start, end, object)
1341 Lisp_Object start, end, object;
d418ef42 1342{
cd7d971d 1343 register INTERVAL i;
03ad6beb 1344 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42
JA
1345 register int s, len, modified;
1346
5fbe2a44 1347 if (NILP (object))
c8a4fc3d 1348 XSETBUFFER (object, current_buffer);
5fbe2a44 1349
d418ef42
JA
1350 i = validate_interval_range (object, &start, &end, soft);
1351 if (NULL_INTERVAL_P (i))
1352 return Qnil;
1353
1354 s = XINT (start);
1355 len = XINT (end) - s;
7855e674 1356
d418ef42
JA
1357 if (i->position != s)
1358 {
7855e674 1359 register int got;
cd7d971d 1360 register INTERVAL unchanged = i;
d418ef42 1361
cdf3e5a2 1362 /* If there are properties here, then this text will be modified. */
cd7d971d 1363 if (! NILP (i->plist))
d418ef42 1364 {
ad9c1940 1365 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 1366 i->plist = Qnil;
d418ef42 1367 modified++;
7855e674
JA
1368
1369 if (LENGTH (i) > len)
1370 {
ad9c1940 1371 i = split_interval_right (i, len);
7855e674
JA
1372 copy_properties (unchanged, i);
1373 return Qt;
1374 }
1375
1376 if (LENGTH (i) == len)
1377 return Qt;
1378
1379 got = LENGTH (i);
d418ef42 1380 }
cd7d971d
JA
1381 /* If the text of I is without any properties, and contains
1382 LEN or more characters, then we may return without changing
1383 anything.*/
7855e674
JA
1384 else if (LENGTH (i) - (s - i->position) <= len)
1385 return Qnil;
cd7d971d 1386 /* The amount of text to change extends past I, so just note
cdf3e5a2 1387 how much we've gotten. */
7855e674
JA
1388 else
1389 got = LENGTH (i) - (s - i->position);
d418ef42
JA
1390
1391 len -= got;
7855e674 1392 prev_changed = i;
d418ef42
JA
1393 i = next_interval (i);
1394 }
1395
cdf3e5a2 1396 /* We are starting at the beginning of an interval, I. */
d418ef42
JA
1397 while (len > 0)
1398 {
7855e674 1399 if (LENGTH (i) >= len)
d418ef42 1400 {
cd7d971d
JA
1401 /* If I has no properties, simply merge it if possible. */
1402 if (NILP (i->plist))
7855e674
JA
1403 {
1404 if (! NULL_INTERVAL_P (prev_changed))
1405 merge_interval_left (i);
d418ef42 1406
7855e674
JA
1407 return modified ? Qt : Qnil;
1408 }
1409
cd7d971d 1410 if (LENGTH (i) > len)
ad9c1940 1411 i = split_interval_left (i, len);
7855e674
JA
1412 if (! NULL_INTERVAL_P (prev_changed))
1413 merge_interval_left (i);
cd7d971d
JA
1414 else
1415 i->plist = Qnil;
7855e674 1416
cd7d971d 1417 return Qt;
d418ef42
JA
1418 }
1419
cd7d971d 1420 /* Here if we still need to erase past the end of I */
d418ef42 1421 len -= LENGTH (i);
7855e674
JA
1422 if (NULL_INTERVAL_P (prev_changed))
1423 {
1424 modified += erase_properties (i);
1425 prev_changed = i;
1426 }
1427 else
1428 {
cd7d971d 1429 modified += ! NILP (i->plist);
cdf3e5a2 1430 /* Merging I will give it the properties of PREV_CHANGED. */
7855e674
JA
1431 prev_changed = i = merge_interval_left (i);
1432 }
1433
d418ef42
JA
1434 i = next_interval (i);
1435 }
1436
1437 return modified ? Qt : Qnil;
1438}
5fbe2a44 1439#endif /* 0 */
d418ef42 1440
15e4954b
JB
1441/* I don't think this is the right interface to export; how often do you
1442 want to do something like this, other than when you're copying objects
1443 around?
1444
1445 I think it would be better to have a pair of functions, one which
1446 returns the text properties of a region as a list of ranges and
1447 plists, and another which applies such a list to another object. */
1448
c98da214
RS
1449/* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1450 SRC and DEST may each refer to strings or buffers.
1451 Optional sixth argument PROP causes only that property to be copied.
1452 Properties are copied to DEST as if by `add-text-properties'.
1453 Return t if any property value actually changed, nil otherwise. */
1454
1455/* Note this can GC when DEST is a buffer. */
fcab51aa 1456\f
15e4954b
JB
1457Lisp_Object
1458copy_text_properties (start, end, src, pos, dest, prop)
1459 Lisp_Object start, end, src, pos, dest, prop;
1460{
1461 INTERVAL i;
1462 Lisp_Object res;
1463 Lisp_Object stuff;
1464 Lisp_Object plist;
1465 int s, e, e2, p, len, modified = 0;
c98da214 1466 struct gcpro gcpro1, gcpro2;
15e4954b
JB
1467
1468 i = validate_interval_range (src, &start, &end, soft);
1469 if (NULL_INTERVAL_P (i))
1470 return Qnil;
1471
1472 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1473 {
1474 Lisp_Object dest_start, dest_end;
1475
1476 dest_start = pos;
e9c4fbcd 1477 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
15e4954b
JB
1478 /* Apply this to a copy of pos; it will try to increment its arguments,
1479 which we don't want. */
1480 validate_interval_range (dest, &dest_start, &dest_end, soft);
1481 }
1482
1483 s = XINT (start);
1484 e = XINT (end);
1485 p = XINT (pos);
1486
1487 stuff = Qnil;
1488
1489 while (s < e)
1490 {
1491 e2 = i->position + LENGTH (i);
1492 if (e2 > e)
1493 e2 = e;
1494 len = e2 - s;
1495
1496 plist = i->plist;
1497 if (! NILP (prop))
1498 while (! NILP (plist))
1499 {
1500 if (EQ (Fcar (plist), prop))
1501 {
1502 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1503 break;
1504 }
1505 plist = Fcdr (Fcdr (plist));
1506 }
1507 if (! NILP (plist))
1508 {
1509 /* Must defer modifications to the interval tree in case src
cdf3e5a2 1510 and dest refer to the same string or buffer. */
15e4954b
JB
1511 stuff = Fcons (Fcons (make_number (p),
1512 Fcons (make_number (p + len),
1513 Fcons (plist, Qnil))),
1514 stuff);
1515 }
1516
1517 i = next_interval (i);
1518 if (NULL_INTERVAL_P (i))
1519 break;
1520
1521 p += len;
1522 s = i->position;
1523 }
1524
c98da214
RS
1525 GCPRO2 (stuff, dest);
1526
15e4954b
JB
1527 while (! NILP (stuff))
1528 {
1529 res = Fcar (stuff);
1530 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1531 Fcar (Fcdr (Fcdr (res))), dest);
1532 if (! NILP (res))
1533 modified++;
1534 stuff = Fcdr (stuff);
1535 }
1536
c98da214
RS
1537 UNGCPRO;
1538
15e4954b
JB
1539 return modified ? Qt : Qnil;
1540}
318d2fa8
RS
1541\f
1542/* Call the modification hook functions in LIST, each with START and END. */
1543
1544static void
1545call_mod_hooks (list, start, end)
1546 Lisp_Object list, start, end;
1547{
1548 struct gcpro gcpro1;
1549 GCPRO1 (list);
1550 while (!NILP (list))
1551 {
1552 call2 (Fcar (list), start, end);
1553 list = Fcdr (list);
1554 }
1555 UNGCPRO;
1556}
1557
96f90544
RS
1558/* Check for read-only intervals between character positions START ... END,
1559 in BUF, and signal an error if we find one.
1560
1561 Then check for any modification hooks in the range.
1562 Create a list of all these hooks in lexicographic order,
1563 eliminating consecutive extra copies of the same hook. Then call
1564 those hooks in order, with START and END - 1 as arguments. */
15e4954b 1565
318d2fa8
RS
1566void
1567verify_interval_modification (buf, start, end)
1568 struct buffer *buf;
1569 int start, end;
1570{
1571 register INTERVAL intervals = BUF_INTERVALS (buf);
1572 register INTERVAL i, prev;
1573 Lisp_Object hooks;
1574 register Lisp_Object prev_mod_hooks;
1575 Lisp_Object mod_hooks;
1576 struct gcpro gcpro1;
1577
1578 hooks = Qnil;
1579 prev_mod_hooks = Qnil;
1580 mod_hooks = Qnil;
1581
1582 interval_insert_behind_hooks = Qnil;
1583 interval_insert_in_front_hooks = Qnil;
1584
1585 if (NULL_INTERVAL_P (intervals))
1586 return;
1587
1588 if (start > end)
1589 {
1590 int temp = start;
1591 start = end;
1592 end = temp;
1593 }
1594
1595 /* For an insert operation, check the two chars around the position. */
1596 if (start == end)
1597 {
1598 INTERVAL prev;
1599 Lisp_Object before, after;
1600
1601 /* Set I to the interval containing the char after START,
1602 and PREV to the interval containing the char before START.
1603 Either one may be null. They may be equal. */
1604 i = find_interval (intervals, start);
1605
1606 if (start == BUF_BEGV (buf))
1607 prev = 0;
1608 else if (i->position == start)
1609 prev = previous_interval (i);
1610 else if (i->position < start)
1611 prev = i;
1612 if (start == BUF_ZV (buf))
1613 i = 0;
1614
1615 /* If Vinhibit_read_only is set and is not a list, we can
1616 skip the read_only checks. */
1617 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
1618 {
1619 /* If I and PREV differ we need to check for the read-only
cdf3e5a2 1620 property together with its stickiness. If either I or
318d2fa8
RS
1621 PREV are 0, this check is all we need.
1622 We have to take special care, since read-only may be
1623 indirectly defined via the category property. */
1624 if (i != prev)
1625 {
1626 if (! NULL_INTERVAL_P (i))
1627 {
1628 after = textget (i->plist, Qread_only);
1629
1630 /* If interval I is read-only and read-only is
1631 front-sticky, inhibit insertion.
1632 Check for read-only as well as category. */
1633 if (! NILP (after)
1634 && NILP (Fmemq (after, Vinhibit_read_only)))
1635 {
1636 Lisp_Object tem;
1637
1638 tem = textget (i->plist, Qfront_sticky);
1639 if (TMEM (Qread_only, tem)
1640 || (NILP (Fplist_get (i->plist, Qread_only))
1641 && TMEM (Qcategory, tem)))
1642 error ("Attempt to insert within read-only text");
1643 }
1644 }
1645
1646 if (! NULL_INTERVAL_P (prev))
1647 {
1648 before = textget (prev->plist, Qread_only);
1649
1650 /* If interval PREV is read-only and read-only isn't
1651 rear-nonsticky, inhibit insertion.
1652 Check for read-only as well as category. */
1653 if (! NILP (before)
1654 && NILP (Fmemq (before, Vinhibit_read_only)))
1655 {
1656 Lisp_Object tem;
1657
1658 tem = textget (prev->plist, Qrear_nonsticky);
1659 if (! TMEM (Qread_only, tem)
1660 && (! NILP (Fplist_get (prev->plist,Qread_only))
1661 || ! TMEM (Qcategory, tem)))
1662 error ("Attempt to insert within read-only text");
1663 }
1664 }
1665 }
1666 else if (! NULL_INTERVAL_P (i))
1667 {
1668 after = textget (i->plist, Qread_only);
1669
1670 /* If interval I is read-only and read-only is
1671 front-sticky, inhibit insertion.
1672 Check for read-only as well as category. */
1673 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
1674 {
1675 Lisp_Object tem;
1676
1677 tem = textget (i->plist, Qfront_sticky);
1678 if (TMEM (Qread_only, tem)
1679 || (NILP (Fplist_get (i->plist, Qread_only))
1680 && TMEM (Qcategory, tem)))
1681 error ("Attempt to insert within read-only text");
1682
1683 tem = textget (prev->plist, Qrear_nonsticky);
1684 if (! TMEM (Qread_only, tem)
1685 && (! NILP (Fplist_get (prev->plist, Qread_only))
1686 || ! TMEM (Qcategory, tem)))
1687 error ("Attempt to insert within read-only text");
1688 }
1689 }
1690 }
1691
1692 /* Run both insert hooks (just once if they're the same). */
1693 if (!NULL_INTERVAL_P (prev))
1694 interval_insert_behind_hooks
1695 = textget (prev->plist, Qinsert_behind_hooks);
1696 if (!NULL_INTERVAL_P (i))
1697 interval_insert_in_front_hooks
1698 = textget (i->plist, Qinsert_in_front_hooks);
1699 }
1700 else
1701 {
1702 /* Loop over intervals on or next to START...END,
1703 collecting their hooks. */
1704
1705 i = find_interval (intervals, start);
1706 do
1707 {
1708 if (! INTERVAL_WRITABLE_P (i))
1709 error ("Attempt to modify read-only text");
1710
1711 mod_hooks = textget (i->plist, Qmodification_hooks);
1712 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1713 {
1714 hooks = Fcons (mod_hooks, hooks);
1715 prev_mod_hooks = mod_hooks;
1716 }
1717
1718 i = next_interval (i);
1719 }
1720 /* Keep going thru the interval containing the char before END. */
1721 while (! NULL_INTERVAL_P (i) && i->position < end);
1722
1723 GCPRO1 (hooks);
1724 hooks = Fnreverse (hooks);
1725 while (! EQ (hooks, Qnil))
1726 {
1727 call_mod_hooks (Fcar (hooks), make_number (start),
1728 make_number (end));
1729 hooks = Fcdr (hooks);
1730 }
1731 UNGCPRO;
1732 }
1733}
1734
96f90544 1735/* Run the interval hooks for an insertion on character range START ... END.
318d2fa8
RS
1736 verify_interval_modification chose which hooks to run;
1737 this function is called after the insertion happens
1738 so it can indicate the range of inserted text. */
1739
1740void
1741report_interval_modification (start, end)
1742 Lisp_Object start, end;
1743{
1744 if (! NILP (interval_insert_behind_hooks))
2e34157c 1745 call_mod_hooks (interval_insert_behind_hooks, start, end);
318d2fa8
RS
1746 if (! NILP (interval_insert_in_front_hooks)
1747 && ! EQ (interval_insert_in_front_hooks,
1748 interval_insert_behind_hooks))
2e34157c 1749 call_mod_hooks (interval_insert_in_front_hooks, start, end);
318d2fa8
RS
1750}
1751\f
d418ef42
JA
1752void
1753syms_of_textprop ()
1754{
ad1b2f20 1755 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
c7dd82a3 1756 "Property-list used as default values.\n\
ad1b2f20
BG
1757The value of a property in this list is seen as the value for every\n\
1758character that does not have its own value for that property.");
1759 Vdefault_text_properties = Qnil;
c7dd82a3 1760
688a5a0f 1761 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
33d7d0df
RS
1762 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1763This also inhibits the use of the `intangible' text property.");
688a5a0f 1764 Vinhibit_point_motion_hooks = Qnil;
318d2fa8
RS
1765
1766 staticpro (&interval_insert_behind_hooks);
1767 staticpro (&interval_insert_in_front_hooks);
1768 interval_insert_behind_hooks = Qnil;
1769 interval_insert_in_front_hooks = Qnil;
1770
688a5a0f 1771
d418ef42
JA
1772 /* Common attributes one might give text */
1773
1774 staticpro (&Qforeground);
1775 Qforeground = intern ("foreground");
1776 staticpro (&Qbackground);
1777 Qbackground = intern ("background");
1778 staticpro (&Qfont);
1779 Qfont = intern ("font");
1780 staticpro (&Qstipple);
1781 Qstipple = intern ("stipple");
1782 staticpro (&Qunderline);
1783 Qunderline = intern ("underline");
1784 staticpro (&Qread_only);
1785 Qread_only = intern ("read-only");
1786 staticpro (&Qinvisible);
1787 Qinvisible = intern ("invisible");
46b4e741
KH
1788 staticpro (&Qintangible);
1789 Qintangible = intern ("intangible");
dc70cea7
RS
1790 staticpro (&Qcategory);
1791 Qcategory = intern ("category");
1792 staticpro (&Qlocal_map);
1793 Qlocal_map = intern ("local-map");
19e1c426
RS
1794 staticpro (&Qfront_sticky);
1795 Qfront_sticky = intern ("front-sticky");
1796 staticpro (&Qrear_nonsticky);
1797 Qrear_nonsticky = intern ("rear-nonsticky");
d418ef42
JA
1798
1799 /* Properties that text might use to specify certain actions */
1800
1801 staticpro (&Qmouse_left);
1802 Qmouse_left = intern ("mouse-left");
1803 staticpro (&Qmouse_entered);
1804 Qmouse_entered = intern ("mouse-entered");
1805 staticpro (&Qpoint_left);
1806 Qpoint_left = intern ("point-left");
1807 staticpro (&Qpoint_entered);
1808 Qpoint_entered = intern ("point-entered");
d418ef42
JA
1809
1810 defsubr (&Stext_properties_at);
5fbe2a44 1811 defsubr (&Sget_text_property);
eb769fd7 1812 defsubr (&Sget_char_property);
fcab51aa
RS
1813 defsubr (&Snext_char_property_change);
1814 defsubr (&Sprevious_char_property_change);
d418ef42 1815 defsubr (&Snext_property_change);
9c79dd1b 1816 defsubr (&Snext_single_property_change);
d418ef42 1817 defsubr (&Sprevious_property_change);
9c79dd1b 1818 defsubr (&Sprevious_single_property_change);
d418ef42 1819 defsubr (&Sadd_text_properties);
d4b530ad 1820 defsubr (&Sput_text_property);
d418ef42
JA
1821 defsubr (&Sset_text_properties);
1822 defsubr (&Sremove_text_properties);
ad9c1940
JB
1823 defsubr (&Stext_property_any);
1824 defsubr (&Stext_property_not_all);
5fbe2a44 1825/* defsubr (&Serase_text_properties); */
15e4954b 1826/* defsubr (&Scopy_text_properties); */
d418ef42 1827}
25013c26
JA
1828
1829#else
1830
1831lose -- this shouldn't be compiled if USE_TEXT_PROPERTIES isn't defined
1832
1833#endif /* USE_TEXT_PROPERTIES */