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