1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
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)
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.
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. */
24 #include "intervals.h"
29 #define NULL (void *)0
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
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.
42 set_properties needs to deal with the interval property cache.
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. */
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
;
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
;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
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
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
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
;
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
;
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. */
88 Fsignal (Qtext_read_only
, Qnil
);
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.
100 When OBJECT is a string, we increment *BEGIN and *END
101 to make them origin-one.
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.
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. */
120 validate_interval_range (object
, begin
, end
, force
)
121 Lisp_Object object
, *begin
, *end
;
127 CHECK_STRING_OR_BUFFER (object
);
128 CHECK_NUMBER_COERCE_MARKER (*begin
);
129 CHECK_NUMBER_COERCE_MARKER (*end
);
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
;
136 if (XINT (*begin
) > XINT (*end
))
144 if (BUFFERP (object
))
146 register struct buffer
*b
= XBUFFER (object
);
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
);
153 /* If there's no text, there are no properties. */
154 if (BUF_BEGV (b
) == BUF_ZV (b
))
155 return NULL_INTERVAL
;
157 searchpos
= XINT (*begin
);
161 register struct Lisp_String
*s
= XSTRING (object
);
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
));
168 XSETFASTINT (*end
, XFASTINT (*end
));
172 return NULL_INTERVAL
;
174 searchpos
= XINT (*begin
);
177 if (NULL_INTERVAL_P (i
))
178 return (force
? create_root_interval (object
) : i
);
180 return find_interval (i
, searchpos
);
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. */
188 validate_plist (list
)
197 register Lisp_Object tail
;
198 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
204 error ("Odd length text property list");
208 return Fcons (list
, Fcons (Qnil
, Qnil
));
211 /* Return nonzero if interval I has all the properties,
212 with the same values, of list PLIST. */
215 interval_has_all_properties (plist
, i
)
219 register Lisp_Object tail1
, tail2
, sym1
;
222 /* Go through each element of PLIST. */
223 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
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
)))
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
))))
237 /* Property has same value on both lists; go to next one. */
249 /* Return nonzero if the plist of interval I has any of the
250 properties of PLIST, regardless of their values. */
253 interval_has_some_properties (plist
, i
)
257 register Lisp_Object tail1
, tail2
, sym
;
259 /* Go through each element of PLIST. */
260 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
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
)))
273 /* Return nonzero if the plist of interval I has any of the
274 property names in LIST, regardless of their values. */
277 interval_has_some_properties_list (list
, i
)
281 register Lisp_Object tail1
, tail2
, sym
;
283 /* Go through each element of LIST. */
284 for (tail1
= list
; ! NILP (tail1
); tail1
= XCDR (tail1
))
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
)))
297 /* Changing the plists of individual intervals. */
299 /* Return the value of PROP in property-list PLIST, or Qunbound if it
302 property_value (plist
, prop
)
303 Lisp_Object plist
, prop
;
307 while (PLIST_ELT_P (plist
, value
))
308 if (EQ (XCAR (plist
), prop
))
311 plist
= XCDR (value
);
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. */
321 set_properties (properties
, interval
, object
)
322 Lisp_Object properties
, object
;
325 Lisp_Object sym
, value
;
327 if (BUFFERP (object
))
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
);
334 if (! EQ (property_value (properties
, XCAR (sym
)),
337 record_property_change (interval
->position
, LENGTH (interval
),
338 XCAR (sym
), XCAR (value
),
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
);
347 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
349 record_property_change (interval
->position
, LENGTH (interval
),
355 /* Store new properties. */
356 interval
->plist
= Fcopy_sequence (properties
);
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.
363 OBJECT should be the string or buffer the interval is in.
365 Return nonzero if this changes I (i.e., if any members of PLIST
366 are actually added to I's plist) */
369 add_properties (plist
, i
, object
)
374 Lisp_Object tail1
, tail2
, sym1
, val1
;
375 register int changed
= 0;
377 struct gcpro gcpro1
, gcpro2
, gcpro3
;
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
);
387 /* Go through each element of PLIST. */
388 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
391 val1
= Fcar (Fcdr (tail1
));
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
)))
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
;
402 this_cdr
= Fcdr (tail2
);
403 /* Found the property. Now check its value. */
406 /* The properties have the same value on both lists.
407 Continue to the next property. */
408 if (EQ (val1
, Fcar (this_cdr
)))
411 /* Record this change in the buffer, for undo purposes. */
412 if (BUFFERP (object
))
414 record_property_change (i
->position
, LENGTH (i
),
415 sym1
, Fcar (this_cdr
), object
);
418 /* I's property has a different value -- change it */
419 Fsetcar (this_cdr
, val1
);
426 /* Record this change in the buffer, for undo purposes. */
427 if (BUFFERP (object
))
429 record_property_change (i
->position
, LENGTH (i
),
432 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
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. */
448 remove_properties (plist
, list
, i
, object
)
449 Lisp_Object plist
, list
;
453 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
454 register int changed
= 0;
456 /* Nonzero means tail1 is a plist, otherwise it is a list. */
459 current_plist
= i
->plist
;
462 tail1
= plist
, use_plist
= 1;
464 tail1
= list
, use_plist
= 0;
466 /* Go through each element of LIST or PLIST. */
467 while (CONSP (tail1
))
471 /* First, remove the symbol if it's at the head of the list */
472 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
474 if (BUFFERP (object
))
475 record_property_change (i
->position
, LENGTH (i
),
476 sym
, XCAR (XCDR (current_plist
)),
479 current_plist
= XCDR (XCDR (current_plist
));
483 /* Go through I's plist, looking for SYM. */
484 tail2
= current_plist
;
485 while (! NILP (tail2
))
487 register Lisp_Object
this;
488 this = XCDR (XCDR (tail2
));
489 if (CONSP (this) && EQ (sym
, XCAR (this)))
491 if (BUFFERP (object
))
492 record_property_change (i
->position
, LENGTH (i
),
493 sym
, XCAR (XCDR (this)), object
);
495 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
501 /* Advance thru TAIL1 one way or the other. */
502 tail1
= XCDR (tail1
);
503 if (use_plist
&& CONSP (tail1
))
504 tail1
= XCDR (tail1
);
508 i
->plist
= current_plist
;
513 /* Remove all properties from interval I. Return non-zero
514 if this changes the interval. */
528 /* Returns the interval of POSITION in OBJECT.
529 POSITION is BEG-based. */
532 interval_of (position
, object
)
540 XSETBUFFER (object
, current_buffer
);
541 else if (EQ (object
, Qt
))
542 return NULL_INTERVAL
;
544 CHECK_STRING_OR_BUFFER (object
);
546 if (BUFFERP (object
))
548 register struct buffer
*b
= XBUFFER (object
);
552 i
= BUF_INTERVALS (b
);
556 register struct Lisp_String
*s
= XSTRING (object
);
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
;
568 return find_interval (i
, position
);
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. */)
578 Lisp_Object position
, object
;
583 XSETBUFFER (object
, current_buffer
);
585 i
= validate_interval_range (object
, &position
, &position
, soft
);
586 if (NULL_INTERVAL_P (i
))
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
)
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
;
606 return textget (Ftext_properties_at (position
, object
), prop
);
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
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
621 get_char_property_and_overlay (position
, prop
, object
, overlay
)
622 Lisp_Object position
, object
;
623 register Lisp_Object prop
;
624 Lisp_Object
*overlay
;
626 struct window
*w
= 0;
628 CHECK_NUMBER_COERCE_MARKER (position
);
631 XSETBUFFER (object
, current_buffer
);
633 if (WINDOWP (object
))
635 w
= XWINDOW (object
);
638 if (BUFFERP (object
))
640 int posn
= XINT (position
);
642 Lisp_Object
*overlay_vec
, tem
;
645 struct buffer
*obuf
= current_buffer
;
647 set_buffer_temp (XBUFFER (object
));
649 /* First try with room for 40 overlays. */
651 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
653 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
654 &next_overlay
, NULL
, 0);
656 /* If there are more than 40,
657 make enough space for all, and try again. */
661 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
662 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
663 &next_overlay
, NULL
, 0);
665 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
667 set_buffer_temp (obuf
);
669 /* Now check the overlays in order of decreasing priority. */
670 while (--noverlays
>= 0)
672 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
676 /* Return the overlay we got the property from. */
677 *overlay
= overlay_vec
[noverlays
];
684 /* Indicate that the return value is not from an overlay. */
687 /* Not a buffer, or no appropriate overlay, so fall through to the
689 return Fget_text_property (position
, prop
, object
);
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
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
;
705 return get_char_property_and_overlay (position
, prop
, object
, 0);
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).
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. */)
719 Lisp_Object position
, limit
;
723 temp
= Fnext_overlay_change (position
);
726 CHECK_NUMBER (limit
);
727 if (XINT (limit
) < XINT (temp
))
730 return Fnext_property_change (position
, Qnil
, temp
);
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
739 If none is found, the function returns (point-max).
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. */)
744 Lisp_Object position
, limit
;
748 temp
= Fprevious_overlay_change (position
);
751 CHECK_NUMBER (limit
);
752 if (XINT (limit
) > XINT (temp
))
755 return Fprevious_property_change (position
, Qnil
, temp
);
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
;
773 if (STRINGP (object
))
775 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
779 position
= make_number (XSTRING (object
)->size
);
786 Lisp_Object initial_value
, value
;
787 int count
= specpdl_ptr
- specpdl
;
790 CHECK_BUFFER (object
);
792 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
794 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
795 Fset_buffer (object
);
798 initial_value
= Fget_char_property (position
, prop
, object
);
801 XSETFASTINT (limit
, BUF_ZV (current_buffer
));
803 CHECK_NUMBER_COERCE_MARKER (limit
);
807 position
= Fnext_char_property_change (position
, limit
);
808 if (XFASTINT (position
) >= XFASTINT (limit
)) {
813 value
= Fget_char_property (position
, prop
, object
);
814 if (!EQ (value
, initial_value
))
818 unbind_to (count
, Qnil
);
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
;
839 if (STRINGP (object
))
841 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
845 position
= make_number (XSTRING (object
)->size
);
852 int count
= specpdl_ptr
- specpdl
;
855 CHECK_BUFFER (object
);
857 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
859 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
860 Fset_buffer (object
);
864 XSETFASTINT (limit
, BUF_BEGV (current_buffer
));
866 CHECK_NUMBER_COERCE_MARKER (limit
);
868 if (XFASTINT (position
) <= XFASTINT (limit
))
872 Lisp_Object initial_value
=
873 Fget_char_property (make_number (XFASTINT (position
) - 1),
878 position
= Fprevious_char_property_change (position
, limit
);
880 if (XFASTINT (position
) <= XFASTINT (limit
))
888 Fget_char_property (make_number (XFASTINT (position
) - 1),
891 if (!EQ (value
, initial_value
))
897 unbind_to (count
, Qnil
);
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.
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
;
917 register INTERVAL i
, next
;
920 XSETBUFFER (object
, current_buffer
);
922 if (!NILP (limit
) && !EQ (limit
, Qt
))
923 CHECK_NUMBER_COERCE_MARKER (limit
);
925 i
= validate_interval_range (object
, &position
, &position
, soft
);
927 /* If LIMIT is t, return start of next interval--don't
928 bother checking further intervals. */
931 if (NULL_INTERVAL_P (i
))
934 next
= next_interval (i
);
936 if (NULL_INTERVAL_P (next
))
937 XSETFASTINT (position
, (STRINGP (object
)
938 ? XSTRING (object
)->size
939 : BUF_ZV (XBUFFER (object
))));
941 XSETFASTINT (position
, next
->position
);
945 if (NULL_INTERVAL_P (i
))
948 next
= next_interval (i
);
950 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
951 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
952 next
= next_interval (next
);
954 if (NULL_INTERVAL_P (next
))
957 XSETFASTINT (limit
, (STRINGP (object
)
958 ? XSTRING (object
)->size
959 : BUF_ZV (XBUFFER (object
))));
960 if (!(next
->position
< XFASTINT (limit
)))
963 XSETFASTINT (position
, next
->position
);
967 /* Return 1 if there's a change in some property between BEG and END. */
970 property_change_between_p (beg
, end
)
973 register INTERVAL i
, next
;
974 Lisp_Object object
, pos
;
976 XSETBUFFER (object
, current_buffer
);
977 XSETFASTINT (pos
, beg
);
979 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
980 if (NULL_INTERVAL_P (i
))
983 next
= next_interval (i
);
984 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
986 next
= next_interval (next
);
987 if (NULL_INTERVAL_P (next
))
989 if (next
->position
>= end
)
993 if (NULL_INTERVAL_P (next
))
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.
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
;
1014 register INTERVAL i
, next
;
1015 register Lisp_Object here_val
;
1018 XSETBUFFER (object
, current_buffer
);
1021 CHECK_NUMBER_COERCE_MARKER (limit
);
1023 i
= validate_interval_range (object
, &position
, &position
, soft
);
1024 if (NULL_INTERVAL_P (i
))
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
);
1034 if (NULL_INTERVAL_P (next
))
1037 XSETFASTINT (limit
, (STRINGP (object
)
1038 ? XSTRING (object
)->size
1039 : BUF_ZV (XBUFFER (object
))));
1040 if (!(next
->position
< XFASTINT (limit
)))
1043 return make_number (next
->position
);
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.
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
;
1060 register INTERVAL i
, previous
;
1063 XSETBUFFER (object
, current_buffer
);
1066 CHECK_NUMBER_COERCE_MARKER (limit
);
1068 i
= validate_interval_range (object
, &position
, &position
, soft
);
1069 if (NULL_INTERVAL_P (i
))
1072 /* Start with the interval containing the char before point. */
1073 if (i
->position
== XFASTINT (position
))
1074 i
= previous_interval (i
);
1076 previous
= previous_interval (i
);
1077 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1079 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1080 previous
= previous_interval (previous
);
1081 if (NULL_INTERVAL_P (previous
))
1084 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1085 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1088 return make_number (previous
->position
+ LENGTH (previous
));
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.
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
;
1106 register INTERVAL i
, previous
;
1107 register Lisp_Object here_val
;
1110 XSETBUFFER (object
, current_buffer
);
1113 CHECK_NUMBER_COERCE_MARKER (limit
);
1115 i
= validate_interval_range (object
, &position
, &position
, soft
);
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
);
1121 if (NULL_INTERVAL_P (i
))
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
))
1129 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1130 previous
= previous_interval (previous
);
1131 if (NULL_INTERVAL_P (previous
))
1134 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1135 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1138 return make_number (previous
->position
+ LENGTH (previous
));
1141 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
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
;
1154 register INTERVAL i
, unchanged
;
1155 register int s
, len
, modified
= 0;
1156 struct gcpro gcpro1
;
1158 properties
= validate_plist (properties
);
1159 if (NILP (properties
))
1163 XSETBUFFER (object
, current_buffer
);
1165 i
= validate_interval_range (object
, &start
, &end
, hard
);
1166 if (NULL_INTERVAL_P (i
))
1170 len
= XINT (end
) - s
;
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
);
1176 /* If we're not starting on an interval boundary, we have to
1177 split this interval. */
1178 if (i
->position
!= s
)
1180 /* If this interval already has the properties, we can
1182 if (interval_has_all_properties (properties
, i
))
1184 int got
= (LENGTH (i
) - (s
- i
->position
));
1186 RETURN_UNGCPRO (Qnil
);
1188 i
= next_interval (i
);
1193 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1194 copy_properties (unchanged
, i
);
1198 if (BUFFERP (object
))
1199 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1201 /* We are at the beginning of interval I, with LEN chars to scan. */
1207 if (LENGTH (i
) >= len
)
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. */
1214 if (interval_has_all_properties (properties
, i
))
1216 if (BUFFERP (object
))
1217 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1218 XINT (end
) - XINT (start
));
1220 return modified
? Qt
: Qnil
;
1223 if (LENGTH (i
) == len
)
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
));
1232 /* i doesn't have the properties, and goes past the change limit */
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
));
1244 modified
+= add_properties (properties
, i
, object
);
1245 i
= next_interval (i
);
1249 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
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
;
1261 Fadd_text_properties (start
, end
,
1262 Fcons (property
, Fcons (value
, Qnil
)),
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
;
1279 return set_text_properties (start
, end
, properties
, object
, Qt
);
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. */
1291 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1292 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1294 register INTERVAL i
;
1295 Lisp_Object ostart
, oend
;
1300 properties
= validate_plist (properties
);
1303 XSETBUFFER (object
, current_buffer
);
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
) == XSTRING (object
)->size
)
1311 if (! XSTRING (object
)->intervals
)
1314 XSTRING (object
)->intervals
= 0;
1318 i
= validate_interval_range (object
, &start
, &end
, soft
);
1320 if (NULL_INTERVAL_P (i
))
1322 /* If buffer has no properties, and we want none, return now. */
1323 if (NILP (properties
))
1326 /* Restore the original START and END values
1327 because validate_interval_range increments them for strings. */
1331 i
= validate_interval_range (object
, &start
, &end
, hard
);
1332 /* This can return if start == end. */
1333 if (NULL_INTERVAL_P (i
))
1337 if (BUFFERP (object
))
1338 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1340 set_text_properties_1 (start
, end
, properties
, object
, i
);
1342 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1343 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1344 XINT (end
) - XINT (start
));
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. */
1356 set_text_properties_1 (start
, end
, properties
, buffer
, i
)
1357 Lisp_Object start
, end
, properties
, buffer
;
1360 register INTERVAL prev_changed
= NULL_INTERVAL
;
1361 register int s
, len
;
1365 len
= XINT (end
) - s
;
1375 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1377 if (i
->position
!= s
)
1380 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1382 if (LENGTH (i
) > len
)
1384 copy_properties (unchanged
, i
);
1385 i
= split_interval_left (i
, len
);
1386 set_properties (properties
, i
, buffer
);
1390 set_properties (properties
, i
, buffer
);
1392 if (LENGTH (i
) == len
)
1397 i
= next_interval (i
);
1400 /* We are starting at the beginning of an interval, I */
1406 if (LENGTH (i
) >= len
)
1408 if (LENGTH (i
) > len
)
1409 i
= split_interval_left (i
, len
);
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
);
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
))
1429 prev_changed
= i
= merge_interval_left (i
);
1431 i
= next_interval (i
);
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
;
1447 register INTERVAL i
, unchanged
;
1448 register int s
, len
, modified
= 0;
1451 XSETBUFFER (object
, current_buffer
);
1453 i
= validate_interval_range (object
, &start
, &end
, soft
);
1454 if (NULL_INTERVAL_P (i
))
1458 len
= XINT (end
) - s
;
1460 if (i
->position
!= s
)
1462 /* No properties on this first interval -- return if
1463 it covers the entire region. */
1464 if (! interval_has_some_properties (properties
, i
))
1466 int got
= (LENGTH (i
) - (s
- i
->position
));
1470 i
= next_interval (i
);
1472 /* Split away the beginning of this interval; what we don't
1477 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1478 copy_properties (unchanged
, i
);
1482 if (BUFFERP (object
))
1483 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1485 /* We are at the beginning of an interval, with len to scan */
1491 if (LENGTH (i
) >= len
)
1493 if (! interval_has_some_properties (properties
, i
))
1494 return modified
? Qt
: Qnil
;
1496 if (LENGTH (i
) == len
)
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
));
1505 /* i has the properties, and goes past the change limit */
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
));
1517 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1518 i
= next_interval (i
);
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
;
1532 register INTERVAL i
, unchanged
;
1533 register int s
, len
, modified
= 0;
1534 Lisp_Object properties
;
1535 properties
= list_of_properties
;
1538 XSETBUFFER (object
, current_buffer
);
1540 i
= validate_interval_range (object
, &start
, &end
, soft
);
1541 if (NULL_INTERVAL_P (i
))
1545 len
= XINT (end
) - s
;
1547 if (i
->position
!= s
)
1549 /* No properties on this first interval -- return if
1550 it covers the entire region. */
1551 if (! interval_has_some_properties_list (properties
, i
))
1553 int got
= (LENGTH (i
) - (s
- i
->position
));
1557 i
= next_interval (i
);
1559 /* Split away the beginning of this interval; what we don't
1564 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1565 copy_properties (unchanged
, i
);
1569 if (BUFFERP (object
))
1570 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1572 /* We are at the beginning of an interval, with len to scan */
1578 if (LENGTH (i
) >= len
)
1580 if (! interval_has_some_properties_list (properties
, i
))
1581 return modified
? Qt
: Qnil
;
1583 if (LENGTH (i
) == len
)
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
));
1592 /* i has the properties, and goes past the change limit */
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
));
1604 modified
+= remove_properties (Qnil
, properties
, i
, object
);
1605 i
= next_interval (i
);
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
;
1619 register INTERVAL i
;
1620 register int e
, pos
;
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
);
1629 while (! NULL_INTERVAL_P (i
))
1631 if (i
->position
>= e
)
1633 if (EQ (textget (i
->plist
, property
), value
))
1636 if (pos
< XINT (start
))
1638 return make_number (pos
);
1640 i
= next_interval (i
);
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
;
1655 register INTERVAL i
;
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
;
1666 while (! NULL_INTERVAL_P (i
))
1668 if (i
->position
>= e
)
1670 if (! EQ (textget (i
->plist
, property
), value
))
1672 if (i
->position
> s
)
1674 return make_number (s
);
1676 i
= next_interval (i
);
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. */
1688 text_property_stickiness (prop
, pos
)
1692 Lisp_Object prev_pos
, front_sticky
;
1693 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1695 if (XINT (pos
) > BEGV
)
1696 /* Consider previous character. */
1698 Lisp_Object rear_non_sticky
;
1700 prev_pos
= make_number (XINT (pos
) - 1);
1701 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, Qnil
);
1703 if (!NILP (CONSP (rear_non_sticky
)
1704 ? Fmemq (prop
, rear_non_sticky
)
1706 /* PROP is rear-non-sticky. */
1710 /* Consider following character. */
1711 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, Qnil
);
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;
1719 /* Simple cases, where the properties are consistent. */
1720 if (is_rear_sticky
&& !is_front_sticky
)
1722 else if (!is_rear_sticky
&& is_front_sticky
)
1724 else if (!is_rear_sticky
&& !is_front_sticky
)
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
)))
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
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. */
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. */
1752 /* Note this can GC when DEST is a buffer. */
1755 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1756 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1762 int s
, e
, e2
, p
, len
, modified
= 0;
1763 struct gcpro gcpro1
, gcpro2
;
1765 i
= validate_interval_range (src
, &start
, &end
, soft
);
1766 if (NULL_INTERVAL_P (i
))
1769 CHECK_NUMBER_COERCE_MARKER (pos
);
1771 Lisp_Object dest_start
, dest_end
;
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
);
1788 e2
= i
->position
+ LENGTH (i
);
1795 while (! NILP (plist
))
1797 if (EQ (Fcar (plist
), prop
))
1799 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1802 plist
= Fcdr (Fcdr (plist
));
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
))),
1814 i
= next_interval (i
);
1815 if (NULL_INTERVAL_P (i
))
1822 GCPRO2 (stuff
, dest
);
1824 while (! NILP (stuff
))
1827 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1828 Fcar (Fcdr (Fcdr (res
))), dest
);
1831 stuff
= Fcdr (stuff
);
1836 return modified
? Qt
: Qnil
;
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. */
1848 text_property_list (object
, start
, end
, prop
)
1849 Lisp_Object object
, start
, end
, prop
;
1856 i
= validate_interval_range (object
, &start
, &end
, soft
);
1857 if (!NULL_INTERVAL_P (i
))
1859 int s
= XINT (start
);
1864 int interval_end
, len
;
1867 interval_end
= i
->position
+ LENGTH (i
);
1868 if (interval_end
> e
)
1870 len
= interval_end
- s
;
1875 for (; !NILP (plist
); plist
= Fcdr (Fcdr (plist
)))
1876 if (EQ (Fcar (plist
), prop
))
1878 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1883 result
= Fcons (Fcons (make_number (s
),
1884 Fcons (make_number (s
+ len
),
1885 Fcons (plist
, Qnil
))),
1888 i
= next_interval (i
);
1889 if (NULL_INTERVAL_P (i
))
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. */
1906 add_text_properties_from_list (object
, list
, delta
)
1907 Lisp_Object object
, list
, delta
;
1909 struct gcpro gcpro1
, gcpro2
;
1912 GCPRO2 (list
, object
);
1914 for (; CONSP (list
); list
= XCDR (list
))
1916 Lisp_Object item
, start
, end
, plist
, tem
;
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
)));
1923 tem
= Fadd_text_properties (start
, end
, plist
, object
);
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. */
1939 extend_property_ranges (list
, old_end
, new_end
)
1940 Lisp_Object list
, old_end
, new_end
;
1942 for (; CONSP (list
); list
= XCDR (list
))
1944 Lisp_Object item
, end
;
1947 end
= XCAR (XCDR (item
));
1949 if (EQ (end
, old_end
))
1950 XSETCAR (XCDR (item
), new_end
);
1956 /* Call the modification hook functions in LIST, each with START and END. */
1959 call_mod_hooks (list
, start
, end
)
1960 Lisp_Object list
, start
, end
;
1962 struct gcpro gcpro1
;
1964 while (!NILP (list
))
1966 call2 (Fcar (list
), start
, end
);
1972 /* Check for read-only intervals between character positions START ... END,
1973 in BUF, and signal an error if we find one.
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. */
1981 verify_interval_modification (buf
, start
, end
)
1985 register INTERVAL intervals
= BUF_INTERVALS (buf
);
1986 register INTERVAL i
;
1988 register Lisp_Object prev_mod_hooks
;
1989 Lisp_Object mod_hooks
;
1990 struct gcpro gcpro1
;
1993 prev_mod_hooks
= Qnil
;
1996 interval_insert_behind_hooks
= Qnil
;
1997 interval_insert_in_front_hooks
= Qnil
;
1999 if (NULL_INTERVAL_P (intervals
))
2009 /* For an insert operation, check the two chars around the position. */
2012 INTERVAL prev
= NULL
;
2013 Lisp_Object before
, after
;
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
);
2020 if (start
== BUF_BEGV (buf
))
2022 else if (i
->position
== start
)
2023 prev
= previous_interval (i
);
2024 else if (i
->position
< start
)
2026 if (start
== BUF_ZV (buf
))
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
))
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. */
2040 if (! NULL_INTERVAL_P (i
))
2042 after
= textget (i
->plist
, Qread_only
);
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. */
2048 && NILP (Fmemq (after
, Vinhibit_read_only
)))
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
)))
2060 if (! NULL_INTERVAL_P (prev
))
2062 before
= textget (prev
->plist
, Qread_only
);
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. */
2068 && NILP (Fmemq (before
, Vinhibit_read_only
)))
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
)))
2080 else if (! NULL_INTERVAL_P (i
))
2082 after
= textget (i
->plist
, Qread_only
);
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
)))
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
)))
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
)))
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
);
2116 /* Loop over intervals on or next to START...END,
2117 collecting their hooks. */
2119 i
= find_interval (intervals
, start
);
2122 if (! INTERVAL_WRITABLE_P (i
))
2125 if (!inhibit_modification_hooks
)
2127 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2128 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2130 hooks
= Fcons (mod_hooks
, hooks
);
2131 prev_mod_hooks
= mod_hooks
;
2135 i
= next_interval (i
);
2137 /* Keep going thru the interval containing the char before END. */
2138 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2140 if (!inhibit_modification_hooks
)
2143 hooks
= Fnreverse (hooks
);
2144 while (! EQ (hooks
, Qnil
))
2146 call_mod_hooks (Fcar (hooks
), make_number (start
),
2148 hooks
= Fcdr (hooks
);
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. */
2161 report_interval_modification (start
, end
)
2162 Lisp_Object start
, end
;
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
);
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
;
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
2188 Vchar_property_alias_alist
= Qnil
;
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
;
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).
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
;
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
;
2212 /* Common attributes one might give text */
2214 staticpro (&Qforeground
);
2215 Qforeground
= intern ("foreground");
2216 staticpro (&Qbackground
);
2217 Qbackground
= intern ("background");
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");
2241 /* Properties that text might use to specify certain actions */
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");
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); */