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