(Fprevious_property_change): Move back at least 1 char.
[bpt/emacs.git] / src / textprop.c
CommitLineData
d418ef42 1/* Interface code for dealing with text properties.
23ad4658 2 Copyright (C) 1993 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
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
18160b98 20#include <config.h>
d418ef42
JA
21#include "lisp.h"
22#include "intervals.h"
23#include "buffer.h"
24\f
25
26/* NOTES: previous- and next- property change will have to skip
27 zero-length intervals if they are implemented. This could be done
28 inside next_interval and previous_interval.
29
9c79dd1b
JA
30 set_properties needs to deal with the interval property cache.
31
d418ef42 32 It is assumed that for any interval plist, a property appears
d4b530ad 33 only once on the list. Although some code i.e., remove_properties,
d418ef42 34 handles the more general case, the uniqueness of properties is
eb8c3be9 35 necessary for the system to remain consistent. This requirement
d418ef42
JA
36 is enforced by the subrs installing properties onto the intervals. */
37
25013c26
JA
38/* The rest of the file is within this conditional */
39#ifdef USE_TEXT_PROPERTIES
d418ef42
JA
40\f
41/* Types of hooks. */
42Lisp_Object Qmouse_left;
43Lisp_Object Qmouse_entered;
44Lisp_Object Qpoint_left;
45Lisp_Object Qpoint_entered;
dc70cea7
RS
46Lisp_Object Qcategory;
47Lisp_Object Qlocal_map;
d418ef42
JA
48
49/* Visual properties text (including strings) may have. */
50Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
19e1c426
RS
51Lisp_Object Qinvisible, Qread_only, Qhidden;
52
53/* Sticky properties */
54Lisp_Object Qfront_sticky, Qrear_nonsticky;
d7b4e137
JB
55
56/* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
57 the o1's cdr. Otherwise, return zero. This is handy for
58 traversing plists. */
59#define PLIST_ELT_P(o1, o2) (CONSP (o1) && CONSP ((o2) = XCONS (o1)->cdr))
60
688a5a0f
RS
61Lisp_Object Vinhibit_point_motion_hooks;
62
d418ef42 63\f
ac876a79
JA
64/* Extract the interval at the position pointed to by BEGIN from
65 OBJECT, a string or buffer. Additionally, check that the positions
66 pointed to by BEGIN and END are within the bounds of OBJECT, and
67 reverse them if *BEGIN is greater than *END. The objects pointed
68 to by BEGIN and END may be integers or markers; if the latter, they
69 are coerced to integers.
d418ef42 70
d4b530ad
RS
71 When OBJECT is a string, we increment *BEGIN and *END
72 to make them origin-one.
73
d418ef42
JA
74 Note that buffer points don't correspond to interval indices.
75 For example, point-max is 1 greater than the index of the last
76 character. This difference is handled in the caller, which uses
77 the validated points to determine a length, and operates on that.
78 Exceptions are Ftext_properties_at, Fnext_property_change, and
79 Fprevious_property_change which call this function with BEGIN == END.
80 Handle this case specially.
81
82 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
ac876a79
JA
83 create an interval tree for OBJECT if one doesn't exist, provided
84 the object actually contains text. In the current design, if there
d4b530ad 85 is no text, there can be no text properties. */
d418ef42
JA
86
87#define soft 0
88#define hard 1
89
90static INTERVAL
91validate_interval_range (object, begin, end, force)
92 Lisp_Object object, *begin, *end;
93 int force;
94{
95 register INTERVAL i;
d4b530ad
RS
96 int searchpos;
97
d418ef42
JA
98 CHECK_STRING_OR_BUFFER (object, 0);
99 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
100 CHECK_NUMBER_COERCE_MARKER (*end, 0);
101
102 /* If we are asked for a point, but from a subr which operates
103 on a range, then return nothing. */
104 if (*begin == *end && begin != end)
105 return NULL_INTERVAL;
106
107 if (XINT (*begin) > XINT (*end))
108 {
d4b530ad
RS
109 Lisp_Object n;
110 n = *begin;
d418ef42 111 *begin = *end;
d4b530ad 112 *end = n;
d418ef42
JA
113 }
114
115 if (XTYPE (object) == Lisp_Buffer)
116 {
117 register struct buffer *b = XBUFFER (object);
118
d418ef42
JA
119 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
120 && XINT (*end) <= BUF_ZV (b)))
121 args_out_of_range (*begin, *end);
122 i = b->intervals;
123
d4b530ad
RS
124 /* If there's no text, there are no properties. */
125 if (BUF_BEGV (b) == BUF_ZV (b))
126 return NULL_INTERVAL;
127
128 searchpos = XINT (*begin);
d418ef42
JA
129 }
130 else
131 {
132 register struct Lisp_String *s = XSTRING (object);
133
d4b530ad 134 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
d418ef42
JA
135 && XINT (*end) <= s->size))
136 args_out_of_range (*begin, *end);
d4b530ad
RS
137 /* User-level Positions in strings start with 0,
138 but the interval code always wants positions starting with 1. */
139 XFASTINT (*begin) += 1;
b1e94638
JB
140 if (begin != end)
141 XFASTINT (*end) += 1;
d418ef42 142 i = s->intervals;
d4b530ad
RS
143
144 if (s->size == 0)
145 return NULL_INTERVAL;
146
147 searchpos = XINT (*begin);
d418ef42
JA
148 }
149
150 if (NULL_INTERVAL_P (i))
151 return (force ? create_root_interval (object) : i);
152
d4b530ad 153 return find_interval (i, searchpos);
d418ef42
JA
154}
155
156/* Validate LIST as a property list. If LIST is not a list, then
157 make one consisting of (LIST nil). Otherwise, verify that LIST
158 is even numbered and thus suitable as a plist. */
159
160static Lisp_Object
161validate_plist (list)
4d780c76 162 Lisp_Object list;
d418ef42
JA
163{
164 if (NILP (list))
165 return Qnil;
166
167 if (CONSP (list))
168 {
169 register int i;
170 register Lisp_Object tail;
171 for (i = 0, tail = list; !NILP (tail); i++)
b1e94638
JB
172 {
173 tail = Fcdr (tail);
174 QUIT;
175 }
d418ef42
JA
176 if (i & 1)
177 error ("Odd length text property list");
178 return list;
179 }
180
181 return Fcons (list, Fcons (Qnil, Qnil));
182}
183
d418ef42
JA
184/* Return nonzero if interval I has all the properties,
185 with the same values, of list PLIST. */
186
187static int
188interval_has_all_properties (plist, i)
189 Lisp_Object plist;
190 INTERVAL i;
191{
192 register Lisp_Object tail1, tail2, sym1, sym2;
193 register int found;
194
195 /* Go through each element of PLIST. */
196 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
197 {
198 sym1 = Fcar (tail1);
199 found = 0;
200
201 /* Go through I's plist, looking for sym1 */
202 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
203 if (EQ (sym1, Fcar (tail2)))
204 {
205 /* Found the same property on both lists. If the
206 values are unequal, return zero. */
734c51b2 207 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
d418ef42
JA
208 return 0;
209
210 /* Property has same value on both lists; go to next one. */
211 found = 1;
212 break;
213 }
214
215 if (! found)
216 return 0;
217 }
218
219 return 1;
220}
221
222/* Return nonzero if the plist of interval I has any of the
223 properties of PLIST, regardless of their values. */
224
225static INLINE int
226interval_has_some_properties (plist, i)
227 Lisp_Object plist;
228 INTERVAL i;
229{
230 register Lisp_Object tail1, tail2, sym;
231
232 /* Go through each element of PLIST. */
233 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
234 {
235 sym = Fcar (tail1);
236
237 /* Go through i's plist, looking for tail1 */
238 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
239 if (EQ (sym, Fcar (tail2)))
240 return 1;
241 }
242
243 return 0;
244}
d4b530ad 245\f
d7b4e137
JB
246/* Changing the plists of individual intervals. */
247
248/* Return the value of PROP in property-list PLIST, or Qunbound if it
249 has none. */
250static int
251property_value (plist, prop)
252{
253 Lisp_Object value;
254
255 while (PLIST_ELT_P (plist, value))
256 if (EQ (XCONS (plist)->car, prop))
257 return XCONS (value)->car;
258 else
259 plist = XCONS (value)->cdr;
260
261 return Qunbound;
262}
263
d4b530ad
RS
264/* Set the properties of INTERVAL to PROPERTIES,
265 and record undo info for the previous values.
266 OBJECT is the string or buffer that INTERVAL belongs to. */
267
268static void
269set_properties (properties, interval, object)
270 Lisp_Object properties, object;
271 INTERVAL interval;
272{
d7b4e137 273 Lisp_Object sym, value;
d4b530ad 274
d7b4e137 275 if (BUFFERP (object))
d4b530ad 276 {
d7b4e137
JB
277 /* For each property in the old plist which is missing from PROPERTIES,
278 or has a different value in PROPERTIES, make an undo record. */
279 for (sym = interval->plist;
280 PLIST_ELT_P (sym, value);
281 sym = XCONS (value)->cdr)
282 if (! EQ (property_value (properties, XCONS (sym)->car),
283 XCONS (value)->car))
f7a9275a
RS
284 {
285 modify_region (XBUFFER (object),
286 make_number (interval->position),
287 make_number (interval->position + LENGTH (interval)));
288 record_property_change (interval->position, LENGTH (interval),
289 XCONS (sym)->car, XCONS (value)->car,
290 object);
291 }
d7b4e137
JB
292
293 /* For each new property that has no value at all in the old plist,
294 make an undo record binding it to nil, so it will be removed. */
295 for (sym = properties;
296 PLIST_ELT_P (sym, value);
297 sym = XCONS (value)->cdr)
298 if (EQ (property_value (interval->plist, XCONS (sym)->car), Qunbound))
f7a9275a
RS
299 {
300 modify_region (XBUFFER (object),
301 make_number (interval->position),
302 make_number (interval->position + LENGTH (interval)));
303 record_property_change (interval->position, LENGTH (interval),
304 XCONS (sym)->car, Qnil,
305 object);
306 }
d4b530ad
RS
307 }
308
309 /* Store new properties. */
310 interval->plist = Fcopy_sequence (properties);
311}
d418ef42
JA
312
313/* Add the properties of PLIST to the interval I, or set
314 the value of I's property to the value of the property on PLIST
315 if they are different.
316
d4b530ad
RS
317 OBJECT should be the string or buffer the interval is in.
318
d418ef42
JA
319 Return nonzero if this changes I (i.e., if any members of PLIST
320 are actually added to I's plist) */
321
d4b530ad
RS
322static int
323add_properties (plist, i, object)
d418ef42
JA
324 Lisp_Object plist;
325 INTERVAL i;
d4b530ad 326 Lisp_Object object;
d418ef42
JA
327{
328 register Lisp_Object tail1, tail2, sym1, val1;
329 register int changed = 0;
330 register int found;
331
332 /* Go through each element of PLIST. */
333 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
334 {
335 sym1 = Fcar (tail1);
336 val1 = Fcar (Fcdr (tail1));
337 found = 0;
338
339 /* Go through I's plist, looking for sym1 */
340 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
341 if (EQ (sym1, Fcar (tail2)))
342 {
343 register Lisp_Object this_cdr = Fcdr (tail2);
344
345 /* Found the property. Now check its value. */
346 found = 1;
347
348 /* The properties have the same value on both lists.
349 Continue to the next property. */
734c51b2 350 if (EQ (val1, Fcar (this_cdr)))
d418ef42
JA
351 break;
352
d4b530ad
RS
353 /* Record this change in the buffer, for undo purposes. */
354 if (XTYPE (object) == Lisp_Buffer)
355 {
04a759c8
JB
356 modify_region (XBUFFER (object),
357 make_number (i->position),
d4b530ad 358 make_number (i->position + LENGTH (i)));
f7a9275a
RS
359 record_property_change (i->position, LENGTH (i),
360 sym1, Fcar (this_cdr), object);
d4b530ad
RS
361 }
362
d418ef42
JA
363 /* I's property has a different value -- change it */
364 Fsetcar (this_cdr, val1);
365 changed++;
366 break;
367 }
368
369 if (! found)
370 {
d4b530ad
RS
371 /* Record this change in the buffer, for undo purposes. */
372 if (XTYPE (object) == Lisp_Buffer)
373 {
04a759c8
JB
374 modify_region (XBUFFER (object),
375 make_number (i->position),
d4b530ad 376 make_number (i->position + LENGTH (i)));
f7a9275a
RS
377 record_property_change (i->position, LENGTH (i),
378 sym1, Qnil, object);
d4b530ad 379 }
d418ef42
JA
380 i->plist = Fcons (sym1, Fcons (val1, i->plist));
381 changed++;
382 }
383 }
384
385 return changed;
386}
387
388/* For any members of PLIST which are properties of I, remove them
d4b530ad
RS
389 from I's plist.
390 OBJECT is the string or buffer containing I. */
d418ef42 391
d4b530ad
RS
392static int
393remove_properties (plist, i, object)
d418ef42
JA
394 Lisp_Object plist;
395 INTERVAL i;
d4b530ad 396 Lisp_Object object;
d418ef42
JA
397{
398 register Lisp_Object tail1, tail2, sym;
399 register Lisp_Object current_plist = i->plist;
400 register int changed = 0;
401
402 /* Go through each element of plist. */
403 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
404 {
405 sym = Fcar (tail1);
406
407 /* First, remove the symbol if its at the head of the list */
408 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
409 {
d4b530ad
RS
410 if (XTYPE (object) == Lisp_Buffer)
411 {
04a759c8
JB
412 modify_region (XBUFFER (object),
413 make_number (i->position),
d4b530ad 414 make_number (i->position + LENGTH (i)));
f7a9275a
RS
415 record_property_change (i->position, LENGTH (i),
416 sym, Fcar (Fcdr (current_plist)),
417 object);
d4b530ad
RS
418 }
419
d418ef42
JA
420 current_plist = Fcdr (Fcdr (current_plist));
421 changed++;
422 }
423
424 /* Go through i's plist, looking for sym */
425 tail2 = current_plist;
426 while (! NILP (tail2))
427 {
428 register Lisp_Object this = Fcdr (Fcdr (tail2));
429 if (EQ (sym, Fcar (this)))
430 {
d4b530ad
RS
431 if (XTYPE (object) == Lisp_Buffer)
432 {
04a759c8
JB
433 modify_region (XBUFFER (object),
434 make_number (i->position),
d4b530ad 435 make_number (i->position + LENGTH (i)));
f7a9275a
RS
436 record_property_change (i->position, LENGTH (i),
437 sym, Fcar (Fcdr (this)), object);
d4b530ad
RS
438 }
439
d418ef42
JA
440 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
441 changed++;
442 }
443 tail2 = this;
444 }
445 }
446
447 if (changed)
448 i->plist = current_plist;
449 return changed;
450}
451
d4b530ad 452#if 0
d418ef42
JA
453/* Remove all properties from interval I. Return non-zero
454 if this changes the interval. */
455
456static INLINE int
457erase_properties (i)
458 INTERVAL i;
459{
460 if (NILP (i->plist))
461 return 0;
462
463 i->plist = Qnil;
464 return 1;
465}
d4b530ad 466#endif
d418ef42 467\f
d418ef42
JA
468DEFUN ("text-properties-at", Ftext_properties_at,
469 Stext_properties_at, 1, 2, 0,
470 "Return the list of properties held by the character at POSITION\n\
471in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
d4b530ad
RS
472defaults to the current buffer.\n\
473If POSITION is at the end of OBJECT, the value is nil.")
d418ef42
JA
474 (pos, object)
475 Lisp_Object pos, object;
476{
477 register INTERVAL i;
d418ef42
JA
478
479 if (NILP (object))
480 XSET (object, Lisp_Buffer, current_buffer);
481
482 i = validate_interval_range (object, &pos, &pos, soft);
483 if (NULL_INTERVAL_P (i))
484 return Qnil;
d4b530ad
RS
485 /* If POS is at the end of the interval,
486 it means it's the end of OBJECT.
487 There are no properties at the very end,
488 since no character follows. */
489 if (XINT (pos) == LENGTH (i) + i->position)
490 return Qnil;
d418ef42
JA
491
492 return i->plist;
493}
494
5fbe2a44 495DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
0df58c91 496 "Return the value of position POS's property PROP, in OBJECT.\n\
d4b530ad
RS
497OBJECT is optional and defaults to the current buffer.\n\
498If POSITION is at the end of OBJECT, the value is nil.")
5fbe2a44 499 (pos, prop, object)
0df58c91 500 Lisp_Object pos, object;
5fbe2a44
RS
501 register Lisp_Object prop;
502{
503 register INTERVAL i;
504 register Lisp_Object tail;
505
506 if (NILP (object))
507 XSET (object, Lisp_Buffer, current_buffer);
5fbe2a44
RS
508 i = validate_interval_range (object, &pos, &pos, soft);
509 if (NULL_INTERVAL_P (i))
510 return Qnil;
511
d4b530ad
RS
512 /* If POS is at the end of the interval,
513 it means it's the end of OBJECT.
514 There are no properties at the very end,
515 since no character follows. */
516 if (XINT (pos) == LENGTH (i) + i->position)
517 return Qnil;
518
dc70cea7 519 return textget (i->plist, prop);
5fbe2a44
RS
520}
521
d418ef42 522DEFUN ("next-property-change", Fnext_property_change,
111b637d 523 Snext_property_change, 1, 3, 0,
5fbe2a44
RS
524 "Return the position of next property change.\n\
525Scans characters forward from POS in OBJECT till it finds\n\
526a change in some text property, then returns the position of the change.\n\
527The optional second argument OBJECT is the string or buffer to scan.\n\
528Return nil if the property is constant all the way to the end of OBJECT.\n\
111b637d
RS
529If the value is non-nil, it is a position greater than POS, never equal.\n\n\
530If the optional third argument LIMIT is non-nil, don't search\n\
531past position LIMIT; return LIMIT if nothing is found before LIMIT.")
532 (pos, object, limit)
533 Lisp_Object pos, object, limit;
d418ef42
JA
534{
535 register INTERVAL i, next;
536
5fbe2a44
RS
537 if (NILP (object))
538 XSET (object, Lisp_Buffer, current_buffer);
539
d418ef42
JA
540 i = validate_interval_range (object, &pos, &pos, soft);
541 if (NULL_INTERVAL_P (i))
111b637d 542 return limit;
d418ef42
JA
543
544 next = next_interval (i);
111b637d
RS
545 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
546 && (NILP (limit) || next->position < XFASTINT (limit)))
d418ef42
JA
547 next = next_interval (next);
548
549 if (NULL_INTERVAL_P (next))
111b637d
RS
550 return limit;
551 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
552 return limit;
d418ef42 553
d4b530ad 554 return next->position - (XTYPE (object) == Lisp_String);
19e1c426
RS
555}
556
557/* Return 1 if there's a change in some property between BEG and END. */
558
559int
560property_change_between_p (beg, end)
561 int beg, end;
562{
563 register INTERVAL i, next;
564 Lisp_Object object, pos;
565
566 XSET (object, Lisp_Buffer, current_buffer);
567 XFASTINT (pos) = beg;
568
569 i = validate_interval_range (object, &pos, &pos, soft);
570 if (NULL_INTERVAL_P (i))
571 return 0;
572
573 next = next_interval (i);
574 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
575 {
576 next = next_interval (next);
e050ef74
RS
577 if (NULL_INTERVAL_P (next))
578 return 0;
19e1c426
RS
579 if (next->position >= end)
580 return 0;
581 }
582
583 if (NULL_INTERVAL_P (next))
584 return 0;
585
586 return 1;
d418ef42
JA
587}
588
9c79dd1b 589DEFUN ("next-single-property-change", Fnext_single_property_change,
111b637d 590 Snext_single_property_change, 2, 4, 0,
5fbe2a44
RS
591 "Return the position of next property change for a specific property.\n\
592Scans characters forward from POS till it finds\n\
593a change in the PROP property, then returns the position of the change.\n\
594The optional third argument OBJECT is the string or buffer to scan.\n\
da625a3c 595The property values are compared with `eq'.\n\
5fbe2a44 596Return nil if the property is constant all the way to the end of OBJECT.\n\
111b637d
RS
597If the value is non-nil, it is a position greater than POS, never equal.\n\n\
598If the optional fourth argument LIMIT is non-nil, don't search\n\
599past position LIMIT; fail if nothing is found before LIMIT.")
600 (pos, prop, object, limit)
601 Lisp_Object pos, prop, object, limit;
9c79dd1b
JA
602{
603 register INTERVAL i, next;
604 register Lisp_Object here_val;
605
5fbe2a44
RS
606 if (NILP (object))
607 XSET (object, Lisp_Buffer, current_buffer);
608
9c79dd1b
JA
609 i = validate_interval_range (object, &pos, &pos, soft);
610 if (NULL_INTERVAL_P (i))
111b637d 611 return limit;
9c79dd1b 612
6a0486dd 613 here_val = textget (i->plist, prop);
9c79dd1b 614 next = next_interval (i);
6a0486dd 615 while (! NULL_INTERVAL_P (next)
111b637d
RS
616 && EQ (here_val, textget (next->plist, prop))
617 && (NILP (limit) || next->position < XFASTINT (limit)))
9c79dd1b
JA
618 next = next_interval (next);
619
620 if (NULL_INTERVAL_P (next))
111b637d
RS
621 return limit;
622 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
623 return limit;
9c79dd1b 624
d4b530ad 625 return next->position - (XTYPE (object) == Lisp_String);
9c79dd1b
JA
626}
627
d418ef42 628DEFUN ("previous-property-change", Fprevious_property_change,
111b637d 629 Sprevious_property_change, 1, 3, 0,
5fbe2a44
RS
630 "Return the position of previous property change.\n\
631Scans characters backwards from POS in OBJECT till it finds\n\
632a change in some text property, then returns the position of the change.\n\
633The optional second argument OBJECT is the string or buffer to scan.\n\
634Return nil if the property is constant all the way to the start of OBJECT.\n\
111b637d
RS
635If the value is non-nil, it is a position less than POS, never equal.\n\n\
636If the optional third argument LIMIT is non-nil, don't search\n\
637back past position LIMIT; fail if nothing is found before LIMIT.")
638 (pos, object, limit)
639 Lisp_Object pos, object, limit;
d418ef42
JA
640{
641 register INTERVAL i, previous;
642
5fbe2a44
RS
643 if (NILP (object))
644 XSET (object, Lisp_Buffer, current_buffer);
645
d418ef42
JA
646 i = validate_interval_range (object, &pos, &pos, soft);
647 if (NULL_INTERVAL_P (i))
111b637d 648 return limit;
d418ef42 649
53b7feec
RS
650 /* Start with the interval containing the char before point. */
651 if (i->position == XFASTINT (pos))
652 i = previous_interval (i);
653
d418ef42 654 previous = previous_interval (i);
111b637d
RS
655 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
656 && (NILP (limit)
657 || previous->position + LENGTH (previous) > XFASTINT (limit)))
d418ef42
JA
658 previous = previous_interval (previous);
659 if (NULL_INTERVAL_P (previous))
111b637d
RS
660 return limit;
661 if (!NILP (limit)
662 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
663 return limit;
d418ef42 664
111b637d 665 return (previous->position + LENGTH (previous)
d4b530ad 666 - (XTYPE (object) == Lisp_String));
d418ef42
JA
667}
668
9c79dd1b 669DEFUN ("previous-single-property-change", Fprevious_single_property_change,
111b637d 670 Sprevious_single_property_change, 2, 4, 0,
5fbe2a44
RS
671 "Return the position of previous property change for a specific property.\n\
672Scans characters backward from POS till it finds\n\
673a change in the PROP property, then returns the position of the change.\n\
674The optional third argument OBJECT is the string or buffer to scan.\n\
93fda178 675The property values are compared with `eq'.\n\
5fbe2a44 676Return nil if the property is constant all the way to the start of OBJECT.\n\
111b637d
RS
677If the value is non-nil, it is a position less than POS, never equal.\n\n\
678If the optional fourth argument LIMIT is non-nil, don't search\n\
679back past position LIMIT; fail if nothing is found before LIMIT.")
680 (pos, prop, object, limit)
681 Lisp_Object pos, prop, object, limit;
9c79dd1b
JA
682{
683 register INTERVAL i, previous;
684 register Lisp_Object here_val;
685
5fbe2a44
RS
686 if (NILP (object))
687 XSET (object, Lisp_Buffer, current_buffer);
688
9c79dd1b
JA
689 i = validate_interval_range (object, &pos, &pos, soft);
690 if (NULL_INTERVAL_P (i))
111b637d 691 return limit;
9c79dd1b 692
53b7feec
RS
693 /* Start with the interval containing the char before point. */
694 if (i->position == XFASTINT (pos))
695 i = previous_interval (i);
696
6a0486dd 697 here_val = textget (i->plist, prop);
9c79dd1b
JA
698 previous = previous_interval (i);
699 while (! NULL_INTERVAL_P (previous)
111b637d
RS
700 && EQ (here_val, textget (previous->plist, prop))
701 && (NILP (limit)
702 || previous->position + LENGTH (previous) > XFASTINT (limit)))
9c79dd1b
JA
703 previous = previous_interval (previous);
704 if (NULL_INTERVAL_P (previous))
111b637d
RS
705 return limit;
706 if (!NILP (limit)
707 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
708 return limit;
9c79dd1b 709
111b637d 710 return (previous->position + LENGTH (previous)
d4b530ad 711 - (XTYPE (object) == Lisp_String));
9c79dd1b
JA
712}
713
d418ef42 714DEFUN ("add-text-properties", Fadd_text_properties,
5fbe2a44
RS
715 Sadd_text_properties, 3, 4, 0,
716 "Add properties to the text from START to END.\n\
717The third argument PROPS is a property list\n\
718specifying the property values to add.\n\
719The optional fourth argument, OBJECT,\n\
720is the string or buffer containing the text.\n\
721Return t if any property value actually changed, nil otherwise.")
722 (start, end, properties, object)
723 Lisp_Object start, end, properties, object;
d418ef42
JA
724{
725 register INTERVAL i, unchanged;
caa31568 726 register int s, len, modified = 0;
d418ef42
JA
727
728 properties = validate_plist (properties);
729 if (NILP (properties))
730 return Qnil;
731
5fbe2a44
RS
732 if (NILP (object))
733 XSET (object, Lisp_Buffer, current_buffer);
734
d418ef42
JA
735 i = validate_interval_range (object, &start, &end, hard);
736 if (NULL_INTERVAL_P (i))
737 return Qnil;
738
739 s = XINT (start);
740 len = XINT (end) - s;
741
742 /* If we're not starting on an interval boundary, we have to
743 split this interval. */
744 if (i->position != s)
745 {
746 /* If this interval already has the properties, we can
747 skip it. */
748 if (interval_has_all_properties (properties, i))
749 {
750 int got = (LENGTH (i) - (s - i->position));
751 if (got >= len)
752 return Qnil;
753 len -= got;
05d5b93e 754 i = next_interval (i);
d418ef42
JA
755 }
756 else
757 {
758 unchanged = i;
ad9c1940 759 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 760 copy_properties (unchanged, i);
d418ef42
JA
761 }
762 }
763
daa5e28f 764 /* We are at the beginning of interval I, with LEN chars to scan. */
caa31568 765 for (;;)
d418ef42 766 {
d4b530ad
RS
767 if (i == 0)
768 abort ();
769
d418ef42
JA
770 if (LENGTH (i) >= len)
771 {
772 if (interval_has_all_properties (properties, i))
773 return modified ? Qt : Qnil;
774
775 if (LENGTH (i) == len)
776 {
d4b530ad 777 add_properties (properties, i, object);
d418ef42
JA
778 return Qt;
779 }
780
781 /* i doesn't have the properties, and goes past the change limit */
782 unchanged = i;
ad9c1940 783 i = split_interval_left (unchanged, len);
d418ef42 784 copy_properties (unchanged, i);
d4b530ad 785 add_properties (properties, i, object);
d418ef42
JA
786 return Qt;
787 }
788
789 len -= LENGTH (i);
d4b530ad 790 modified += add_properties (properties, i, object);
d418ef42
JA
791 i = next_interval (i);
792 }
793}
794
d4b530ad
RS
795DEFUN ("put-text-property", Fput_text_property,
796 Sput_text_property, 4, 5, 0,
797 "Set one property of the text from START to END.\n\
798The third and fourth arguments PROP and VALUE\n\
799specify the property to add.\n\
800The optional fifth argument, OBJECT,\n\
801is the string or buffer containing the text.")
802 (start, end, prop, value, object)
803 Lisp_Object start, end, prop, value, object;
804{
805 Fadd_text_properties (start, end,
806 Fcons (prop, Fcons (value, Qnil)),
807 object);
808 return Qnil;
809}
810
d418ef42 811DEFUN ("set-text-properties", Fset_text_properties,
5fbe2a44
RS
812 Sset_text_properties, 3, 4, 0,
813 "Completely replace properties of text from START to END.\n\
814The third argument PROPS is the new property list.\n\
815The optional fourth argument, OBJECT,\n\
816is the string or buffer containing the text.")
817 (start, end, props, object)
818 Lisp_Object start, end, props, object;
d418ef42
JA
819{
820 register INTERVAL i, unchanged;
9c79dd1b 821 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42
JA
822 register int s, len;
823
5fbe2a44 824 props = validate_plist (props);
d418ef42 825
5fbe2a44
RS
826 if (NILP (object))
827 XSET (object, Lisp_Buffer, current_buffer);
828
d418ef42
JA
829 i = validate_interval_range (object, &start, &end, hard);
830 if (NULL_INTERVAL_P (i))
831 return Qnil;
832
833 s = XINT (start);
834 len = XINT (end) - s;
835
836 if (i->position != s)
837 {
838 unchanged = i;
ad9c1940 839 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 840
d418ef42
JA
841 if (LENGTH (i) > len)
842 {
9c79dd1b 843 copy_properties (unchanged, i);
ad9c1940 844 i = split_interval_left (i, len);
daa5e28f 845 set_properties (props, i, object);
d418ef42
JA
846 return Qt;
847 }
848
daa5e28f
RS
849 set_properties (props, i, object);
850
9c79dd1b
JA
851 if (LENGTH (i) == len)
852 return Qt;
853
854 prev_changed = i;
d418ef42
JA
855 len -= LENGTH (i);
856 i = next_interval (i);
857 }
858
cd7d971d 859 /* We are starting at the beginning of an interval, I */
7855e674 860 while (len > 0)
d418ef42 861 {
d4b530ad
RS
862 if (i == 0)
863 abort ();
864
d418ef42
JA
865 if (LENGTH (i) >= len)
866 {
cd7d971d 867 if (LENGTH (i) > len)
ad9c1940 868 i = split_interval_left (i, len);
d418ef42 869
9c79dd1b 870 if (NULL_INTERVAL_P (prev_changed))
d4b530ad 871 set_properties (props, i, object);
9c79dd1b
JA
872 else
873 merge_interval_left (i);
d418ef42
JA
874 return Qt;
875 }
876
877 len -= LENGTH (i);
9c79dd1b
JA
878 if (NULL_INTERVAL_P (prev_changed))
879 {
d4b530ad 880 set_properties (props, i, object);
9c79dd1b
JA
881 prev_changed = i;
882 }
883 else
884 prev_changed = i = merge_interval_left (i);
885
d418ef42
JA
886 i = next_interval (i);
887 }
888
889 return Qt;
890}
891
892DEFUN ("remove-text-properties", Fremove_text_properties,
5fbe2a44
RS
893 Sremove_text_properties, 3, 4, 0,
894 "Remove some properties from text from START to END.\n\
895The third argument PROPS is a property list\n\
896whose property names specify the properties to remove.\n\
897\(The values stored in PROPS are ignored.)\n\
898The optional fourth argument, OBJECT,\n\
899is the string or buffer containing the text.\n\
900Return t if any property was actually removed, nil otherwise.")
901 (start, end, props, object)
902 Lisp_Object start, end, props, object;
d418ef42
JA
903{
904 register INTERVAL i, unchanged;
caa31568 905 register int s, len, modified = 0;
d418ef42 906
5fbe2a44
RS
907 if (NILP (object))
908 XSET (object, Lisp_Buffer, current_buffer);
909
d418ef42
JA
910 i = validate_interval_range (object, &start, &end, soft);
911 if (NULL_INTERVAL_P (i))
912 return Qnil;
913
914 s = XINT (start);
915 len = XINT (end) - s;
9c79dd1b 916
d418ef42
JA
917 if (i->position != s)
918 {
919 /* No properties on this first interval -- return if
920 it covers the entire region. */
5fbe2a44 921 if (! interval_has_some_properties (props, i))
d418ef42
JA
922 {
923 int got = (LENGTH (i) - (s - i->position));
924 if (got >= len)
925 return Qnil;
926 len -= got;
05d5b93e 927 i = next_interval (i);
d418ef42 928 }
daa5e28f
RS
929 /* Split away the beginning of this interval; what we don't
930 want to modify. */
d418ef42
JA
931 else
932 {
933 unchanged = i;
ad9c1940 934 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 935 copy_properties (unchanged, i);
d418ef42
JA
936 }
937 }
938
939 /* We are at the beginning of an interval, with len to scan */
caa31568 940 for (;;)
d418ef42 941 {
d4b530ad
RS
942 if (i == 0)
943 abort ();
944
d418ef42
JA
945 if (LENGTH (i) >= len)
946 {
5fbe2a44 947 if (! interval_has_some_properties (props, i))
d418ef42
JA
948 return modified ? Qt : Qnil;
949
950 if (LENGTH (i) == len)
951 {
d4b530ad 952 remove_properties (props, i, object);
d418ef42
JA
953 return Qt;
954 }
955
956 /* i has the properties, and goes past the change limit */
daa5e28f 957 unchanged = i;
ad9c1940 958 i = split_interval_left (i, len);
d418ef42 959 copy_properties (unchanged, i);
d4b530ad 960 remove_properties (props, i, object);
d418ef42
JA
961 return Qt;
962 }
963
964 len -= LENGTH (i);
d4b530ad 965 modified += remove_properties (props, i, object);
d418ef42
JA
966 i = next_interval (i);
967 }
968}
969
ad9c1940
JB
970DEFUN ("text-property-any", Ftext_property_any,
971 Stext_property_any, 4, 5, 0,
972 "Check text from START to END to see if PROP is ever `eq' to VALUE.\n\
973If so, return the position of the first character whose PROP is `eq'\n\
974to VALUE. Otherwise return nil.\n\
975The optional fifth argument, OBJECT, is the string or buffer\n\
976containing the text.")
977 (start, end, prop, value, object)
978 Lisp_Object start, end, prop, value, object;
979{
980 register INTERVAL i;
981 register int e, pos;
982
983 if (NILP (object))
984 XSET (object, Lisp_Buffer, current_buffer);
985 i = validate_interval_range (object, &start, &end, soft);
986 e = XINT (end);
987
988 while (! NULL_INTERVAL_P (i))
989 {
990 if (i->position >= e)
991 break;
992 if (EQ (textget (i->plist, prop), value))
993 {
994 pos = i->position;
995 if (pos < XINT (start))
996 pos = XINT (start);
997 return make_number (pos - (XTYPE (object) == Lisp_String));
998 }
999 i = next_interval (i);
1000 }
1001 return Qnil;
1002}
1003
1004DEFUN ("text-property-not-all", Ftext_property_not_all,
1005 Stext_property_not_all, 4, 5, 0,
1006 "Check text from START to END to see if PROP is ever not `eq' to VALUE.\n\
1007If so, return the position of the first character whose PROP is not\n\
1008`eq' to VALUE. Otherwise, return nil.\n\
1009The optional fifth argument, OBJECT, is the string or buffer\n\
1010containing the text.")
1011 (start, end, prop, value, object)
1012 Lisp_Object start, end, prop, value, object;
1013{
1014 register INTERVAL i;
1015 register int s, e;
1016
1017 if (NILP (object))
1018 XSET (object, Lisp_Buffer, current_buffer);
1019 i = validate_interval_range (object, &start, &end, soft);
1020 if (NULL_INTERVAL_P (i))
916a3119 1021 return (NILP (value) || EQ (start, end)) ? Qnil : start;
ad9c1940
JB
1022 s = XINT (start);
1023 e = XINT (end);
1024
1025 while (! NULL_INTERVAL_P (i))
1026 {
1027 if (i->position >= e)
1028 break;
1029 if (! EQ (textget (i->plist, prop), value))
1030 {
1031 if (i->position > s)
1032 s = i->position;
1033 return make_number (s - (XTYPE (object) == Lisp_String));
1034 }
1035 i = next_interval (i);
1036 }
1037 return Qnil;
1038}
1039
5fbe2a44
RS
1040#if 0 /* You can use set-text-properties for this. */
1041
d418ef42 1042DEFUN ("erase-text-properties", Ferase_text_properties,
5fbe2a44
RS
1043 Serase_text_properties, 2, 3, 0,
1044 "Remove all properties from the text from START to END.\n\
1045The optional third argument, OBJECT,\n\
1046is the string or buffer containing the text.")
1047 (start, end, object)
1048 Lisp_Object start, end, object;
d418ef42 1049{
cd7d971d 1050 register INTERVAL i;
03ad6beb 1051 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42
JA
1052 register int s, len, modified;
1053
5fbe2a44
RS
1054 if (NILP (object))
1055 XSET (object, Lisp_Buffer, current_buffer);
1056
d418ef42
JA
1057 i = validate_interval_range (object, &start, &end, soft);
1058 if (NULL_INTERVAL_P (i))
1059 return Qnil;
1060
1061 s = XINT (start);
1062 len = XINT (end) - s;
7855e674 1063
d418ef42
JA
1064 if (i->position != s)
1065 {
7855e674 1066 register int got;
cd7d971d 1067 register INTERVAL unchanged = i;
d418ef42 1068
7855e674 1069 /* If there are properties here, then this text will be modified. */
cd7d971d 1070 if (! NILP (i->plist))
d418ef42 1071 {
ad9c1940 1072 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 1073 i->plist = Qnil;
d418ef42 1074 modified++;
7855e674
JA
1075
1076 if (LENGTH (i) > len)
1077 {
ad9c1940 1078 i = split_interval_right (i, len);
7855e674
JA
1079 copy_properties (unchanged, i);
1080 return Qt;
1081 }
1082
1083 if (LENGTH (i) == len)
1084 return Qt;
1085
1086 got = LENGTH (i);
d418ef42 1087 }
cd7d971d
JA
1088 /* If the text of I is without any properties, and contains
1089 LEN or more characters, then we may return without changing
1090 anything.*/
7855e674
JA
1091 else if (LENGTH (i) - (s - i->position) <= len)
1092 return Qnil;
cd7d971d
JA
1093 /* The amount of text to change extends past I, so just note
1094 how much we've gotten. */
7855e674
JA
1095 else
1096 got = LENGTH (i) - (s - i->position);
d418ef42
JA
1097
1098 len -= got;
7855e674 1099 prev_changed = i;
d418ef42
JA
1100 i = next_interval (i);
1101 }
1102
7855e674 1103 /* We are starting at the beginning of an interval, I. */
d418ef42
JA
1104 while (len > 0)
1105 {
7855e674 1106 if (LENGTH (i) >= len)
d418ef42 1107 {
cd7d971d
JA
1108 /* If I has no properties, simply merge it if possible. */
1109 if (NILP (i->plist))
7855e674
JA
1110 {
1111 if (! NULL_INTERVAL_P (prev_changed))
1112 merge_interval_left (i);
d418ef42 1113
7855e674
JA
1114 return modified ? Qt : Qnil;
1115 }
1116
cd7d971d 1117 if (LENGTH (i) > len)
ad9c1940 1118 i = split_interval_left (i, len);
7855e674
JA
1119 if (! NULL_INTERVAL_P (prev_changed))
1120 merge_interval_left (i);
cd7d971d
JA
1121 else
1122 i->plist = Qnil;
7855e674 1123
cd7d971d 1124 return Qt;
d418ef42
JA
1125 }
1126
cd7d971d 1127 /* Here if we still need to erase past the end of I */
d418ef42 1128 len -= LENGTH (i);
7855e674
JA
1129 if (NULL_INTERVAL_P (prev_changed))
1130 {
1131 modified += erase_properties (i);
1132 prev_changed = i;
1133 }
1134 else
1135 {
cd7d971d
JA
1136 modified += ! NILP (i->plist);
1137 /* Merging I will give it the properties of PREV_CHANGED. */
7855e674
JA
1138 prev_changed = i = merge_interval_left (i);
1139 }
1140
d418ef42
JA
1141 i = next_interval (i);
1142 }
1143
1144 return modified ? Qt : Qnil;
1145}
5fbe2a44 1146#endif /* 0 */
d418ef42 1147
15e4954b
JB
1148/* I don't think this is the right interface to export; how often do you
1149 want to do something like this, other than when you're copying objects
1150 around?
1151
1152 I think it would be better to have a pair of functions, one which
1153 returns the text properties of a region as a list of ranges and
1154 plists, and another which applies such a list to another object. */
1155
1156/* DEFUN ("copy-text-properties", Fcopy_text_properties,
1157 Scopy_text_properties, 5, 6, 0,
1158 "Add properties from SRC-START to SRC-END of SRC at DEST-POS of DEST.\n\
1159SRC and DEST may each refer to strings or buffers.\n\
1160Optional sixth argument PROP causes only that property to be copied.\n\
1161Properties are copied to DEST as if by `add-text-properties'.\n\
1162Return t if any property value actually changed, nil otherwise.") */
1163
1164Lisp_Object
1165copy_text_properties (start, end, src, pos, dest, prop)
1166 Lisp_Object start, end, src, pos, dest, prop;
1167{
1168 INTERVAL i;
1169 Lisp_Object res;
1170 Lisp_Object stuff;
1171 Lisp_Object plist;
1172 int s, e, e2, p, len, modified = 0;
1173
1174 i = validate_interval_range (src, &start, &end, soft);
1175 if (NULL_INTERVAL_P (i))
1176 return Qnil;
1177
1178 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1179 {
1180 Lisp_Object dest_start, dest_end;
1181
1182 dest_start = pos;
1183 XFASTINT (dest_end) = XINT (dest_start) + (XINT (end) - XINT (start));
1184 /* Apply this to a copy of pos; it will try to increment its arguments,
1185 which we don't want. */
1186 validate_interval_range (dest, &dest_start, &dest_end, soft);
1187 }
1188
1189 s = XINT (start);
1190 e = XINT (end);
1191 p = XINT (pos);
1192
1193 stuff = Qnil;
1194
1195 while (s < e)
1196 {
1197 e2 = i->position + LENGTH (i);
1198 if (e2 > e)
1199 e2 = e;
1200 len = e2 - s;
1201
1202 plist = i->plist;
1203 if (! NILP (prop))
1204 while (! NILP (plist))
1205 {
1206 if (EQ (Fcar (plist), prop))
1207 {
1208 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1209 break;
1210 }
1211 plist = Fcdr (Fcdr (plist));
1212 }
1213 if (! NILP (plist))
1214 {
1215 /* Must defer modifications to the interval tree in case src
1216 and dest refer to the same string or buffer. */
1217 stuff = Fcons (Fcons (make_number (p),
1218 Fcons (make_number (p + len),
1219 Fcons (plist, Qnil))),
1220 stuff);
1221 }
1222
1223 i = next_interval (i);
1224 if (NULL_INTERVAL_P (i))
1225 break;
1226
1227 p += len;
1228 s = i->position;
1229 }
1230
1231 while (! NILP (stuff))
1232 {
1233 res = Fcar (stuff);
1234 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1235 Fcar (Fcdr (Fcdr (res))), dest);
1236 if (! NILP (res))
1237 modified++;
1238 stuff = Fcdr (stuff);
1239 }
1240
1241 return modified ? Qt : Qnil;
1242}
1243
d418ef42
JA
1244void
1245syms_of_textprop ()
1246{
1247 DEFVAR_INT ("interval-balance-threshold", &interval_balance_threshold,
c2e42adb 1248 "Threshold for rebalancing interval trees, expressed as the\n\
d418ef42
JA
1249percentage by which the left interval tree should not differ from the right.");
1250 interval_balance_threshold = 8;
1251
688a5a0f
RS
1252 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
1253 "If nonnil, don't call the text property values of\n\
1254`point-left' and `point-entered'.");
1255 Vinhibit_point_motion_hooks = Qnil;
1256
d418ef42
JA
1257 /* Common attributes one might give text */
1258
1259 staticpro (&Qforeground);
1260 Qforeground = intern ("foreground");
1261 staticpro (&Qbackground);
1262 Qbackground = intern ("background");
1263 staticpro (&Qfont);
1264 Qfont = intern ("font");
1265 staticpro (&Qstipple);
1266 Qstipple = intern ("stipple");
1267 staticpro (&Qunderline);
1268 Qunderline = intern ("underline");
1269 staticpro (&Qread_only);
1270 Qread_only = intern ("read-only");
1271 staticpro (&Qinvisible);
1272 Qinvisible = intern ("invisible");
19e1c426
RS
1273 staticpro (&Qhidden);
1274 Qhidden = intern ("hidden");
dc70cea7
RS
1275 staticpro (&Qcategory);
1276 Qcategory = intern ("category");
1277 staticpro (&Qlocal_map);
1278 Qlocal_map = intern ("local-map");
19e1c426
RS
1279 staticpro (&Qfront_sticky);
1280 Qfront_sticky = intern ("front-sticky");
1281 staticpro (&Qrear_nonsticky);
1282 Qrear_nonsticky = intern ("rear-nonsticky");
d418ef42
JA
1283
1284 /* Properties that text might use to specify certain actions */
1285
1286 staticpro (&Qmouse_left);
1287 Qmouse_left = intern ("mouse-left");
1288 staticpro (&Qmouse_entered);
1289 Qmouse_entered = intern ("mouse-entered");
1290 staticpro (&Qpoint_left);
1291 Qpoint_left = intern ("point-left");
1292 staticpro (&Qpoint_entered);
1293 Qpoint_entered = intern ("point-entered");
d418ef42
JA
1294
1295 defsubr (&Stext_properties_at);
5fbe2a44 1296 defsubr (&Sget_text_property);
d418ef42 1297 defsubr (&Snext_property_change);
9c79dd1b 1298 defsubr (&Snext_single_property_change);
d418ef42 1299 defsubr (&Sprevious_property_change);
9c79dd1b 1300 defsubr (&Sprevious_single_property_change);
d418ef42 1301 defsubr (&Sadd_text_properties);
d4b530ad 1302 defsubr (&Sput_text_property);
d418ef42
JA
1303 defsubr (&Sset_text_properties);
1304 defsubr (&Sremove_text_properties);
ad9c1940
JB
1305 defsubr (&Stext_property_any);
1306 defsubr (&Stext_property_not_all);
5fbe2a44 1307/* defsubr (&Serase_text_properties); */
15e4954b 1308/* defsubr (&Scopy_text_properties); */
d418ef42 1309}
25013c26
JA
1310
1311#else
1312
1313lose -- this shouldn't be compiled if USE_TEXT_PROPERTIES isn't defined
1314
1315#endif /* USE_TEXT_PROPERTIES */