1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003
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. */
86 text_read_only (propval
)
89 Fsignal (Qtext_read_only
, STRINGP (propval
) ? Fcons (propval
, Qnil
) : Qnil
);
94 /* Extract the interval at the position pointed to by BEGIN from
95 OBJECT, a string or buffer. Additionally, check that the positions
96 pointed to by BEGIN and END are within the bounds of OBJECT, and
97 reverse them if *BEGIN is greater than *END. The objects pointed
98 to by BEGIN and END may be integers or markers; if the latter, they
99 are coerced to integers.
101 When OBJECT is a string, we increment *BEGIN and *END
102 to make them origin-one.
104 Note that buffer points don't correspond to interval indices.
105 For example, point-max is 1 greater than the index of the last
106 character. This difference is handled in the caller, which uses
107 the validated points to determine a length, and operates on that.
108 Exceptions are Ftext_properties_at, Fnext_property_change, and
109 Fprevious_property_change which call this function with BEGIN == END.
110 Handle this case specially.
112 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
113 create an interval tree for OBJECT if one doesn't exist, provided
114 the object actually contains text. In the current design, if there
115 is no text, there can be no text properties. */
121 validate_interval_range (object
, begin
, end
, force
)
122 Lisp_Object object
, *begin
, *end
;
128 CHECK_STRING_OR_BUFFER (object
);
129 CHECK_NUMBER_COERCE_MARKER (*begin
);
130 CHECK_NUMBER_COERCE_MARKER (*end
);
132 /* If we are asked for a point, but from a subr which operates
133 on a range, then return nothing. */
134 if (EQ (*begin
, *end
) && begin
!= end
)
135 return NULL_INTERVAL
;
137 if (XINT (*begin
) > XINT (*end
))
145 if (BUFFERP (object
))
147 register struct buffer
*b
= XBUFFER (object
);
149 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
150 && XINT (*end
) <= BUF_ZV (b
)))
151 args_out_of_range (*begin
, *end
);
152 i
= BUF_INTERVALS (b
);
154 /* If there's no text, there are no properties. */
155 if (BUF_BEGV (b
) == BUF_ZV (b
))
156 return NULL_INTERVAL
;
158 searchpos
= XINT (*begin
);
162 int len
= SCHARS (object
);
164 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
165 && XINT (*end
) <= len
))
166 args_out_of_range (*begin
, *end
);
167 XSETFASTINT (*begin
, XFASTINT (*begin
));
169 XSETFASTINT (*end
, XFASTINT (*end
));
170 i
= STRING_INTERVALS (object
);
173 return NULL_INTERVAL
;
175 searchpos
= XINT (*begin
);
178 if (NULL_INTERVAL_P (i
))
179 return (force
? create_root_interval (object
) : i
);
181 return find_interval (i
, searchpos
);
184 /* Validate LIST as a property list. If LIST is not a list, then
185 make one consisting of (LIST nil). Otherwise, verify that LIST
186 is even numbered and thus suitable as a plist. */
189 validate_plist (list
)
198 register Lisp_Object tail
;
199 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
205 error ("Odd length text property list");
209 return Fcons (list
, Fcons (Qnil
, Qnil
));
212 /* Return nonzero if interval I has all the properties,
213 with the same values, of list PLIST. */
216 interval_has_all_properties (plist
, i
)
220 register Lisp_Object tail1
, tail2
, sym1
;
223 /* Go through each element of PLIST. */
224 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
229 /* Go through I's plist, looking for sym1 */
230 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
231 if (EQ (sym1
, Fcar (tail2
)))
233 /* Found the same property on both lists. If the
234 values are unequal, return zero. */
235 if (! EQ (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))))
238 /* Property has same value on both lists; go to next one. */
250 /* Return nonzero if the plist of interval I has any of the
251 properties of PLIST, regardless of their values. */
254 interval_has_some_properties (plist
, i
)
258 register Lisp_Object tail1
, tail2
, sym
;
260 /* Go through each element of PLIST. */
261 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
265 /* Go through i's plist, looking for tail1 */
266 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
267 if (EQ (sym
, Fcar (tail2
)))
274 /* Return nonzero if the plist of interval I has any of the
275 property names in LIST, regardless of their values. */
278 interval_has_some_properties_list (list
, i
)
282 register Lisp_Object tail1
, tail2
, sym
;
284 /* Go through each element of LIST. */
285 for (tail1
= list
; ! NILP (tail1
); tail1
= XCDR (tail1
))
289 /* Go through i's plist, looking for tail1 */
290 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= XCDR (XCDR (tail2
)))
291 if (EQ (sym
, XCAR (tail2
)))
298 /* Changing the plists of individual intervals. */
300 /* Return the value of PROP in property-list PLIST, or Qunbound if it
303 property_value (plist
, prop
)
304 Lisp_Object plist
, prop
;
308 while (PLIST_ELT_P (plist
, value
))
309 if (EQ (XCAR (plist
), prop
))
312 plist
= XCDR (value
);
317 /* Set the properties of INTERVAL to PROPERTIES,
318 and record undo info for the previous values.
319 OBJECT is the string or buffer that INTERVAL belongs to. */
322 set_properties (properties
, interval
, object
)
323 Lisp_Object properties
, object
;
326 Lisp_Object sym
, value
;
328 if (BUFFERP (object
))
330 /* For each property in the old plist which is missing from PROPERTIES,
331 or has a different value in PROPERTIES, make an undo record. */
332 for (sym
= interval
->plist
;
333 PLIST_ELT_P (sym
, value
);
335 if (! EQ (property_value (properties
, XCAR (sym
)),
338 record_property_change (interval
->position
, LENGTH (interval
),
339 XCAR (sym
), XCAR (value
),
343 /* For each new property that has no value at all in the old plist,
344 make an undo record binding it to nil, so it will be removed. */
345 for (sym
= properties
;
346 PLIST_ELT_P (sym
, value
);
348 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
350 record_property_change (interval
->position
, LENGTH (interval
),
356 /* Store new properties. */
357 interval
->plist
= Fcopy_sequence (properties
);
360 /* Add the properties of PLIST to the interval I, or set
361 the value of I's property to the value of the property on PLIST
362 if they are different.
364 OBJECT should be the string or buffer the interval is in.
366 Return nonzero if this changes I (i.e., if any members of PLIST
367 are actually added to I's plist) */
370 add_properties (plist
, i
, object
)
375 Lisp_Object tail1
, tail2
, sym1
, val1
;
376 register int changed
= 0;
378 struct gcpro gcpro1
, gcpro2
, gcpro3
;
383 /* No need to protect OBJECT, because we can GC only in the case
384 where it is a buffer, and live buffers are always protected.
385 I and its plist are also protected, via OBJECT. */
386 GCPRO3 (tail1
, sym1
, val1
);
388 /* Go through each element of PLIST. */
389 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
392 val1
= Fcar (Fcdr (tail1
));
395 /* Go through I's plist, looking for sym1 */
396 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
397 if (EQ (sym1
, Fcar (tail2
)))
399 /* No need to gcpro, because tail2 protects this
400 and it must be a cons cell (we get an error otherwise). */
401 register Lisp_Object this_cdr
;
403 this_cdr
= Fcdr (tail2
);
404 /* Found the property. Now check its value. */
407 /* The properties have the same value on both lists.
408 Continue to the next property. */
409 if (EQ (val1
, Fcar (this_cdr
)))
412 /* Record this change in the buffer, for undo purposes. */
413 if (BUFFERP (object
))
415 record_property_change (i
->position
, LENGTH (i
),
416 sym1
, Fcar (this_cdr
), object
);
419 /* I's property has a different value -- change it */
420 Fsetcar (this_cdr
, val1
);
427 /* Record this change in the buffer, for undo purposes. */
428 if (BUFFERP (object
))
430 record_property_change (i
->position
, LENGTH (i
),
433 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
443 /* For any members of PLIST, or LIST,
444 which are properties of I, remove them from I's plist.
445 (If PLIST is non-nil, use that, otherwise use LIST.)
446 OBJECT is the string or buffer containing I. */
449 remove_properties (plist
, list
, i
, object
)
450 Lisp_Object plist
, list
;
454 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
455 register int changed
= 0;
457 /* Nonzero means tail1 is a plist, otherwise it is a list. */
460 current_plist
= i
->plist
;
463 tail1
= plist
, use_plist
= 1;
465 tail1
= list
, use_plist
= 0;
467 /* Go through each element of LIST or PLIST. */
468 while (CONSP (tail1
))
472 /* First, remove the symbol if it's at the head of the list */
473 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
475 if (BUFFERP (object
))
476 record_property_change (i
->position
, LENGTH (i
),
477 sym
, XCAR (XCDR (current_plist
)),
480 current_plist
= XCDR (XCDR (current_plist
));
484 /* Go through I's plist, looking for SYM. */
485 tail2
= current_plist
;
486 while (! NILP (tail2
))
488 register Lisp_Object
this;
489 this = XCDR (XCDR (tail2
));
490 if (CONSP (this) && EQ (sym
, XCAR (this)))
492 if (BUFFERP (object
))
493 record_property_change (i
->position
, LENGTH (i
),
494 sym
, XCAR (XCDR (this)), object
);
496 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
502 /* Advance thru TAIL1 one way or the other. */
503 tail1
= XCDR (tail1
);
504 if (use_plist
&& CONSP (tail1
))
505 tail1
= XCDR (tail1
);
509 i
->plist
= current_plist
;
514 /* Remove all properties from interval I. Return non-zero
515 if this changes the interval. */
529 /* Returns the interval of POSITION in OBJECT.
530 POSITION is BEG-based. */
533 interval_of (position
, object
)
541 XSETBUFFER (object
, current_buffer
);
542 else if (EQ (object
, Qt
))
543 return NULL_INTERVAL
;
545 CHECK_STRING_OR_BUFFER (object
);
547 if (BUFFERP (object
))
549 register struct buffer
*b
= XBUFFER (object
);
553 i
= BUF_INTERVALS (b
);
558 end
= SCHARS (object
);
559 i
= STRING_INTERVALS (object
);
562 if (!(beg
<= position
&& position
<= end
))
563 args_out_of_range (make_number (position
), make_number (position
));
564 if (beg
== end
|| NULL_INTERVAL_P (i
))
565 return NULL_INTERVAL
;
567 return find_interval (i
, position
);
570 DEFUN ("text-properties-at", Ftext_properties_at
,
571 Stext_properties_at
, 1, 2, 0,
572 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
573 If the optional second argument OBJECT is a buffer (or nil, which means
574 the current buffer), POSITION is a buffer position (integer or marker).
575 If OBJECT is a string, POSITION is a 0-based index into it.
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 char's property PROP, in OBJECT at POSITION.
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
;
644 struct buffer
*obuf
= current_buffer
;
646 set_buffer_temp (XBUFFER (object
));
648 /* First try with room for 40 overlays. */
650 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
652 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
655 /* If there are more than 40,
656 make enough space for all, and try again. */
660 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
661 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
664 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
666 set_buffer_temp (obuf
);
668 /* Now check the overlays in order of decreasing priority. */
669 while (--noverlays
>= 0)
671 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
675 /* Return the overlay we got the property from. */
676 *overlay
= overlay_vec
[noverlays
];
683 /* Indicate that the return value is not from an overlay. */
686 /* Not a buffer, or no appropriate overlay, so fall through to the
688 return Fget_text_property (position
, prop
, object
);
691 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
692 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
693 Both overlay properties and text properties are checked.
694 OBJECT is optional and defaults to the current buffer.
695 If POSITION is at the end of OBJECT, the value is nil.
696 If OBJECT is a buffer, then overlay properties are considered as well as
698 If OBJECT is a window, then that window's buffer is used, but window-specific
699 overlays are considered only if they are associated with OBJECT. */)
700 (position
, prop
, object
)
701 Lisp_Object position
, object
;
702 register Lisp_Object prop
;
704 return get_char_property_and_overlay (position
, prop
, object
, 0);
707 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
708 Sget_char_property_and_overlay
, 2, 3, 0,
709 doc
: /* Like `get-char-property', but with extra overlay information.
710 Return a cons whose car is the return value of `get-char-property'
711 with the same arguments, that is, the value of POSITION's property
712 PROP in OBJECT, and whose cdr is the overlay in which the property was
713 found, or nil, if it was found as a text property or not found at all.
714 OBJECT is optional and defaults to the current buffer. OBJECT may be
715 a string, a buffer or a window. For strings, the cdr of the return
716 value is always nil, since strings do not have overlays. If OBJECT is
717 a window, then that window's buffer is used, but window-specific
718 overlays are considered only if they are associated with OBJECT. If
719 POSITION is at the end of OBJECT, both car and cdr are nil. */)
720 (position
, prop
, object
)
721 Lisp_Object position
, object
;
722 register Lisp_Object prop
;
726 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
727 return Fcons(val
, overlay
);
731 DEFUN ("next-char-property-change", Fnext_char_property_change
,
732 Snext_char_property_change
, 1, 2, 0,
733 doc
: /* Return the position of next text property or overlay change.
734 This scans characters forward in the current buffer from POSITION till
735 it finds a change in some text property, or the beginning or end of an
736 overlay, and returns the position of that.
737 If none is found, the function returns (point-max).
739 If the optional third argument LIMIT is non-nil, don't search
740 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
742 Lisp_Object position
, limit
;
746 temp
= Fnext_overlay_change (position
);
749 CHECK_NUMBER (limit
);
750 if (XINT (limit
) < XINT (temp
))
753 return Fnext_property_change (position
, Qnil
, temp
);
756 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
757 Sprevious_char_property_change
, 1, 2, 0,
758 doc
: /* Return the position of previous text property or overlay change.
759 Scans characters backward in the current buffer from POSITION till it
760 finds a change in some text property, or the beginning or end of an
761 overlay, and returns the position of that.
762 If none is found, the function returns (point-max).
764 If the optional third argument LIMIT is non-nil, don't search
765 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
767 Lisp_Object position
, limit
;
771 temp
= Fprevious_overlay_change (position
);
774 CHECK_NUMBER (limit
);
775 if (XINT (limit
) > XINT (temp
))
778 return Fprevious_property_change (position
, Qnil
, temp
);
782 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
783 Snext_single_char_property_change
, 2, 4, 0,
784 doc
: /* Return the position of next text property or overlay change for a specific property.
785 Scans characters forward from POSITION till it finds
786 a change in the PROP property, then returns the position of the change.
787 If the optional third argument OBJECT is a buffer (or nil, which means
788 the current buffer), POSITION is a buffer position (integer or marker).
789 If OBJECT is a string, POSITION is a 0-based index into it.
791 The property values are compared with `eq'.
792 If the property is constant all the way to the end of OBJECT, return the
793 last valid position in OBJECT.
794 If the optional fourth argument LIMIT is non-nil, don't search
795 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
796 (position
, prop
, object
, limit
)
797 Lisp_Object prop
, position
, object
, limit
;
799 if (STRINGP (object
))
801 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
805 position
= make_number (SCHARS (object
));
812 Lisp_Object initial_value
, value
;
813 int count
= SPECPDL_INDEX ();
816 CHECK_BUFFER (object
);
818 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
820 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
821 Fset_buffer (object
);
824 initial_value
= Fget_char_property (position
, prop
, object
);
827 XSETFASTINT (limit
, BUF_ZV (current_buffer
));
829 CHECK_NUMBER_COERCE_MARKER (limit
);
833 position
= Fnext_char_property_change (position
, limit
);
834 if (XFASTINT (position
) >= XFASTINT (limit
)) {
839 value
= Fget_char_property (position
, prop
, object
);
840 if (!EQ (value
, initial_value
))
844 unbind_to (count
, Qnil
);
850 DEFUN ("previous-single-char-property-change",
851 Fprevious_single_char_property_change
,
852 Sprevious_single_char_property_change
, 2, 4, 0,
853 doc
: /* Return the position of previous text property or overlay change for a specific property.
854 Scans characters backward from POSITION till it finds
855 a change in the PROP property, then returns the position of the change.
856 If the optional third argument OBJECT is a buffer (or nil, which means
857 the current buffer), POSITION is a buffer position (integer or marker).
858 If OBJECT is a string, POSITION is a 0-based index into it.
860 The property values are compared with `eq'.
861 If the property is constant all the way to the start of OBJECT, return the
862 first valid position in OBJECT.
863 If the optional fourth argument LIMIT is non-nil, don't search
864 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
865 (position
, prop
, object
, limit
)
866 Lisp_Object prop
, position
, object
, limit
;
868 if (STRINGP (object
))
870 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
874 position
= make_number (SCHARS (object
));
881 int count
= SPECPDL_INDEX ();
884 CHECK_BUFFER (object
);
886 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
888 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
889 Fset_buffer (object
);
893 XSETFASTINT (limit
, BUF_BEGV (current_buffer
));
895 CHECK_NUMBER_COERCE_MARKER (limit
);
897 if (XFASTINT (position
) <= XFASTINT (limit
))
901 Lisp_Object initial_value
=
902 Fget_char_property (make_number (XFASTINT (position
) - 1),
907 position
= Fprevious_char_property_change (position
, limit
);
909 if (XFASTINT (position
) <= XFASTINT (limit
))
917 Fget_char_property (make_number (XFASTINT (position
) - 1),
920 if (!EQ (value
, initial_value
))
926 unbind_to (count
, Qnil
);
932 DEFUN ("next-property-change", Fnext_property_change
,
933 Snext_property_change
, 1, 3, 0,
934 doc
: /* Return the position of next property change.
935 Scans characters forward from POSITION in OBJECT till it finds
936 a change in some text property, then returns the position of the change.
937 If the optional second argument OBJECT is a buffer (or nil, which means
938 the current buffer), POSITION is a buffer position (integer or marker).
939 If OBJECT is a string, POSITION is a 0-based index into it.
940 Return nil if the property is constant all the way to the end of OBJECT.
941 If the value is non-nil, it is a position greater than POSITION, never equal.
943 If the optional third argument LIMIT is non-nil, don't search
944 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
945 (position
, object
, limit
)
946 Lisp_Object position
, object
, limit
;
948 register INTERVAL i
, next
;
951 XSETBUFFER (object
, current_buffer
);
953 if (!NILP (limit
) && !EQ (limit
, Qt
))
954 CHECK_NUMBER_COERCE_MARKER (limit
);
956 i
= validate_interval_range (object
, &position
, &position
, soft
);
958 /* If LIMIT is t, return start of next interval--don't
959 bother checking further intervals. */
962 if (NULL_INTERVAL_P (i
))
965 next
= next_interval (i
);
967 if (NULL_INTERVAL_P (next
))
968 XSETFASTINT (position
, (STRINGP (object
)
970 : BUF_ZV (XBUFFER (object
))));
972 XSETFASTINT (position
, next
->position
);
976 if (NULL_INTERVAL_P (i
))
979 next
= next_interval (i
);
981 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
982 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
983 next
= next_interval (next
);
985 if (NULL_INTERVAL_P (next
))
988 XSETFASTINT (limit
, (STRINGP (object
)
990 : BUF_ZV (XBUFFER (object
))));
991 if (!(next
->position
< XFASTINT (limit
)))
994 XSETFASTINT (position
, next
->position
);
998 /* Return 1 if there's a change in some property between BEG and END. */
1001 property_change_between_p (beg
, end
)
1004 register INTERVAL i
, next
;
1005 Lisp_Object object
, pos
;
1007 XSETBUFFER (object
, current_buffer
);
1008 XSETFASTINT (pos
, beg
);
1010 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
1011 if (NULL_INTERVAL_P (i
))
1014 next
= next_interval (i
);
1015 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
1017 next
= next_interval (next
);
1018 if (NULL_INTERVAL_P (next
))
1020 if (next
->position
>= end
)
1024 if (NULL_INTERVAL_P (next
))
1030 DEFUN ("next-single-property-change", Fnext_single_property_change
,
1031 Snext_single_property_change
, 2, 4, 0,
1032 doc
: /* Return the position of next property change for a specific property.
1033 Scans characters forward from POSITION till it finds
1034 a change in the PROP property, then returns the position of the change.
1035 If the optional third argument OBJECT is a buffer (or nil, which means
1036 the current buffer), POSITION is a buffer position (integer or marker).
1037 If OBJECT is a string, POSITION is a 0-based index into it.
1038 The property values are compared with `eq'.
1039 Return nil if the property is constant all the way to the end of OBJECT.
1040 If the value is non-nil, it is a position greater than POSITION, never equal.
1042 If the optional fourth argument LIMIT is non-nil, don't search
1043 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1044 (position
, prop
, object
, limit
)
1045 Lisp_Object position
, prop
, object
, limit
;
1047 register INTERVAL i
, next
;
1048 register Lisp_Object here_val
;
1051 XSETBUFFER (object
, current_buffer
);
1054 CHECK_NUMBER_COERCE_MARKER (limit
);
1056 i
= validate_interval_range (object
, &position
, &position
, soft
);
1057 if (NULL_INTERVAL_P (i
))
1060 here_val
= textget (i
->plist
, prop
);
1061 next
= next_interval (i
);
1062 while (! NULL_INTERVAL_P (next
)
1063 && EQ (here_val
, textget (next
->plist
, prop
))
1064 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1065 next
= next_interval (next
);
1067 if (NULL_INTERVAL_P (next
))
1070 XSETFASTINT (limit
, (STRINGP (object
)
1072 : BUF_ZV (XBUFFER (object
))));
1073 if (!(next
->position
< XFASTINT (limit
)))
1076 return make_number (next
->position
);
1079 DEFUN ("previous-property-change", Fprevious_property_change
,
1080 Sprevious_property_change
, 1, 3, 0,
1081 doc
: /* Return the position of previous property change.
1082 Scans characters backwards from POSITION in OBJECT till it finds
1083 a change in some text property, then returns the position of the change.
1084 If the optional second argument OBJECT is a buffer (or nil, which means
1085 the current buffer), POSITION is a buffer position (integer or marker).
1086 If OBJECT is a string, POSITION is a 0-based index into it.
1087 Return nil if the property is constant all the way to the start of OBJECT.
1088 If the value is non-nil, it is a position less than POSITION, never equal.
1090 If the optional third argument LIMIT is non-nil, don't search
1091 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1092 (position
, object
, limit
)
1093 Lisp_Object position
, object
, limit
;
1095 register INTERVAL i
, previous
;
1098 XSETBUFFER (object
, current_buffer
);
1101 CHECK_NUMBER_COERCE_MARKER (limit
);
1103 i
= validate_interval_range (object
, &position
, &position
, soft
);
1104 if (NULL_INTERVAL_P (i
))
1107 /* Start with the interval containing the char before point. */
1108 if (i
->position
== XFASTINT (position
))
1109 i
= previous_interval (i
);
1111 previous
= previous_interval (i
);
1112 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1114 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1115 previous
= previous_interval (previous
);
1116 if (NULL_INTERVAL_P (previous
))
1119 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1120 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1123 return make_number (previous
->position
+ LENGTH (previous
));
1126 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1127 Sprevious_single_property_change
, 2, 4, 0,
1128 doc
: /* Return the position of previous property change for a specific property.
1129 Scans characters backward from POSITION till it finds
1130 a change in the PROP property, then returns the position of the change.
1131 If the optional third argument OBJECT is a buffer (or nil, which means
1132 the current buffer), POSITION is a buffer position (integer or marker).
1133 If OBJECT is a string, POSITION is a 0-based index into it.
1134 The property values are compared with `eq'.
1135 Return nil if the property is constant all the way to the start of OBJECT.
1136 If the value is non-nil, it is a position less than POSITION, never equal.
1138 If the optional fourth argument LIMIT is non-nil, don't search
1139 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1140 (position
, prop
, object
, limit
)
1141 Lisp_Object position
, prop
, object
, limit
;
1143 register INTERVAL i
, previous
;
1144 register Lisp_Object here_val
;
1147 XSETBUFFER (object
, current_buffer
);
1150 CHECK_NUMBER_COERCE_MARKER (limit
);
1152 i
= validate_interval_range (object
, &position
, &position
, soft
);
1154 /* Start with the interval containing the char before point. */
1155 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1156 i
= previous_interval (i
);
1158 if (NULL_INTERVAL_P (i
))
1161 here_val
= textget (i
->plist
, prop
);
1162 previous
= previous_interval (i
);
1163 while (!NULL_INTERVAL_P (previous
)
1164 && EQ (here_val
, textget (previous
->plist
, prop
))
1166 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1167 previous
= previous_interval (previous
);
1168 if (NULL_INTERVAL_P (previous
))
1171 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1172 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1175 return make_number (previous
->position
+ LENGTH (previous
));
1178 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1180 DEFUN ("add-text-properties", Fadd_text_properties
,
1181 Sadd_text_properties
, 3, 4, 0,
1182 doc
: /* Add properties to the text from START to END.
1183 The third argument PROPERTIES is a property list
1184 specifying the property values to add. If the optional fourth argument
1185 OBJECT is a buffer (or nil, which means the current buffer),
1186 START and END are buffer positions (integers or markers).
1187 If OBJECT is a string, START and END are 0-based indices into it.
1188 Return t if any property value actually changed, nil otherwise. */)
1189 (start
, end
, properties
, object
)
1190 Lisp_Object start
, end
, properties
, object
;
1192 register INTERVAL i
, unchanged
;
1193 register int s
, len
, modified
= 0;
1194 struct gcpro gcpro1
;
1196 properties
= validate_plist (properties
);
1197 if (NILP (properties
))
1201 XSETBUFFER (object
, current_buffer
);
1203 i
= validate_interval_range (object
, &start
, &end
, hard
);
1204 if (NULL_INTERVAL_P (i
))
1208 len
= XINT (end
) - s
;
1210 /* No need to protect OBJECT, because we GC only if it's a buffer,
1211 and live buffers are always protected. */
1212 GCPRO1 (properties
);
1214 /* If we're not starting on an interval boundary, we have to
1215 split this interval. */
1216 if (i
->position
!= s
)
1218 /* If this interval already has the properties, we can
1220 if (interval_has_all_properties (properties
, i
))
1222 int got
= (LENGTH (i
) - (s
- i
->position
));
1224 RETURN_UNGCPRO (Qnil
);
1226 i
= next_interval (i
);
1231 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1232 copy_properties (unchanged
, i
);
1236 if (BUFFERP (object
))
1237 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1239 /* We are at the beginning of interval I, with LEN chars to scan. */
1245 if (LENGTH (i
) >= len
)
1247 /* We can UNGCPRO safely here, because there will be just
1248 one more chance to gc, in the next call to add_properties,
1249 and after that we will not need PROPERTIES or OBJECT again. */
1252 if (interval_has_all_properties (properties
, i
))
1254 if (BUFFERP (object
))
1255 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1256 XINT (end
) - XINT (start
));
1258 return modified
? Qt
: Qnil
;
1261 if (LENGTH (i
) == len
)
1263 add_properties (properties
, i
, object
);
1264 if (BUFFERP (object
))
1265 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1266 XINT (end
) - XINT (start
));
1270 /* i doesn't have the properties, and goes past the change limit */
1272 i
= split_interval_left (unchanged
, len
);
1273 copy_properties (unchanged
, i
);
1274 add_properties (properties
, i
, object
);
1275 if (BUFFERP (object
))
1276 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1277 XINT (end
) - XINT (start
));
1282 modified
+= add_properties (properties
, i
, object
);
1283 i
= next_interval (i
);
1287 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1289 DEFUN ("put-text-property", Fput_text_property
,
1290 Sput_text_property
, 4, 5, 0,
1291 doc
: /* Set one property of the text from START to END.
1292 The third and fourth arguments PROPERTY and VALUE
1293 specify the property to add.
1294 If the optional fifth argument OBJECT is a buffer (or nil, which means
1295 the current buffer), START and END are buffer positions (integers or
1296 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1297 (start
, end
, property
, value
, object
)
1298 Lisp_Object start
, end
, property
, value
, object
;
1300 Fadd_text_properties (start
, end
,
1301 Fcons (property
, Fcons (value
, Qnil
)),
1306 DEFUN ("set-text-properties", Fset_text_properties
,
1307 Sset_text_properties
, 3, 4, 0,
1308 doc
: /* Completely replace properties of text from START to END.
1309 The third argument PROPERTIES is the new property list.
1310 If the optional fourth argument OBJECT is a buffer (or nil, which means
1311 the current buffer), START and END are buffer positions (integers or
1312 markers). If OBJECT is a string, START and END are 0-based indices into it.
1313 If PROPERTIES is nil, the effect is to remove all properties from
1314 the designated part of OBJECT. */)
1315 (start
, end
, properties
, object
)
1316 Lisp_Object start
, end
, properties
, object
;
1318 return set_text_properties (start
, end
, properties
, object
, Qt
);
1322 /* Replace properties of text from START to END with new list of
1323 properties PROPERTIES. OBJECT is the buffer or string containing
1324 the text. OBJECT nil means use the current buffer.
1325 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1326 is non-nil if properties were replaced; it is nil if there weren't
1327 any properties to replace. */
1330 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1331 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1333 register INTERVAL i
;
1334 Lisp_Object ostart
, oend
;
1339 properties
= validate_plist (properties
);
1342 XSETBUFFER (object
, current_buffer
);
1344 /* If we want no properties for a whole string,
1345 get rid of its intervals. */
1346 if (NILP (properties
) && STRINGP (object
)
1347 && XFASTINT (start
) == 0
1348 && XFASTINT (end
) == SCHARS (object
))
1350 if (! STRING_INTERVALS (object
))
1353 STRING_SET_INTERVALS (object
, NULL_INTERVAL
);
1357 i
= validate_interval_range (object
, &start
, &end
, soft
);
1359 if (NULL_INTERVAL_P (i
))
1361 /* If buffer has no properties, and we want none, return now. */
1362 if (NILP (properties
))
1365 /* Restore the original START and END values
1366 because validate_interval_range increments them for strings. */
1370 i
= validate_interval_range (object
, &start
, &end
, hard
);
1371 /* This can return if start == end. */
1372 if (NULL_INTERVAL_P (i
))
1376 if (BUFFERP (object
))
1377 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1379 set_text_properties_1 (start
, end
, properties
, object
, i
);
1381 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1382 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1383 XINT (end
) - XINT (start
));
1387 /* Replace properties of text from START to END with new list of
1388 properties PROPERTIES. BUFFER is the buffer containing
1389 the text. This does not obey any hooks.
1390 You can provide the interval that START is located in as I,
1391 or pass NULL for I and this function will find it.
1392 START and END can be in any order. */
1395 set_text_properties_1 (start
, end
, properties
, buffer
, i
)
1396 Lisp_Object start
, end
, properties
, buffer
;
1399 register INTERVAL prev_changed
= NULL_INTERVAL
;
1400 register int s
, len
;
1404 len
= XINT (end
) - s
;
1414 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1416 if (i
->position
!= s
)
1419 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1421 if (LENGTH (i
) > len
)
1423 copy_properties (unchanged
, i
);
1424 i
= split_interval_left (i
, len
);
1425 set_properties (properties
, i
, buffer
);
1429 set_properties (properties
, i
, buffer
);
1431 if (LENGTH (i
) == len
)
1436 i
= next_interval (i
);
1439 /* We are starting at the beginning of an interval, I */
1445 if (LENGTH (i
) >= len
)
1447 if (LENGTH (i
) > len
)
1448 i
= split_interval_left (i
, len
);
1450 /* We have to call set_properties even if we are going to
1451 merge the intervals, so as to make the undo records
1452 and cause redisplay to happen. */
1453 set_properties (properties
, i
, buffer
);
1454 if (!NULL_INTERVAL_P (prev_changed
))
1455 merge_interval_left (i
);
1461 /* We have to call set_properties even if we are going to
1462 merge the intervals, so as to make the undo records
1463 and cause redisplay to happen. */
1464 set_properties (properties
, i
, buffer
);
1465 if (NULL_INTERVAL_P (prev_changed
))
1468 prev_changed
= i
= merge_interval_left (i
);
1470 i
= next_interval (i
);
1474 DEFUN ("remove-text-properties", Fremove_text_properties
,
1475 Sremove_text_properties
, 3, 4, 0,
1476 doc
: /* Remove some properties from text from START to END.
1477 The third argument PROPERTIES is a property list
1478 whose property names specify the properties to remove.
1479 \(The values stored in PROPERTIES are ignored.)
1480 If the optional fourth argument OBJECT is a buffer (or nil, which means
1481 the current buffer), START and END are buffer positions (integers or
1482 markers). If OBJECT is a string, START and END are 0-based indices into it.
1483 Return t if any property was actually removed, nil otherwise.
1485 Use set-text-properties if you want to remove all text properties. */)
1486 (start
, end
, properties
, object
)
1487 Lisp_Object start
, end
, properties
, object
;
1489 register INTERVAL i
, unchanged
;
1490 register int s
, len
, modified
= 0;
1493 XSETBUFFER (object
, current_buffer
);
1495 i
= validate_interval_range (object
, &start
, &end
, soft
);
1496 if (NULL_INTERVAL_P (i
))
1500 len
= XINT (end
) - s
;
1502 if (i
->position
!= s
)
1504 /* No properties on this first interval -- return if
1505 it covers the entire region. */
1506 if (! interval_has_some_properties (properties
, i
))
1508 int got
= (LENGTH (i
) - (s
- i
->position
));
1512 i
= next_interval (i
);
1514 /* Split away the beginning of this interval; what we don't
1519 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1520 copy_properties (unchanged
, i
);
1524 if (BUFFERP (object
))
1525 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1527 /* We are at the beginning of an interval, with len to scan */
1533 if (LENGTH (i
) >= len
)
1535 if (! interval_has_some_properties (properties
, i
))
1536 return modified
? Qt
: Qnil
;
1538 if (LENGTH (i
) == len
)
1540 remove_properties (properties
, Qnil
, i
, object
);
1541 if (BUFFERP (object
))
1542 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1543 XINT (end
) - XINT (start
));
1547 /* i has the properties, and goes past the change limit */
1549 i
= split_interval_left (i
, len
);
1550 copy_properties (unchanged
, i
);
1551 remove_properties (properties
, Qnil
, i
, object
);
1552 if (BUFFERP (object
))
1553 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1554 XINT (end
) - XINT (start
));
1559 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1560 i
= next_interval (i
);
1564 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1565 Sremove_list_of_text_properties
, 3, 4, 0,
1566 doc
: /* Remove some properties from text from START to END.
1567 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1568 If the optional fourth argument OBJECT is a buffer (or nil, which means
1569 the current buffer), START and END are buffer positions (integers or
1570 markers). If OBJECT is a string, START and END are 0-based indices into it.
1571 Return t if any property was actually removed, nil otherwise. */)
1572 (start
, end
, list_of_properties
, object
)
1573 Lisp_Object start
, end
, list_of_properties
, object
;
1575 register INTERVAL i
, unchanged
;
1576 register int s
, len
, modified
= 0;
1577 Lisp_Object properties
;
1578 properties
= list_of_properties
;
1581 XSETBUFFER (object
, current_buffer
);
1583 i
= validate_interval_range (object
, &start
, &end
, soft
);
1584 if (NULL_INTERVAL_P (i
))
1588 len
= XINT (end
) - s
;
1590 if (i
->position
!= s
)
1592 /* No properties on this first interval -- return if
1593 it covers the entire region. */
1594 if (! interval_has_some_properties_list (properties
, i
))
1596 int got
= (LENGTH (i
) - (s
- i
->position
));
1600 i
= next_interval (i
);
1602 /* Split away the beginning of this interval; what we don't
1607 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1608 copy_properties (unchanged
, i
);
1612 if (BUFFERP (object
))
1613 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1615 /* We are at the beginning of an interval, with len to scan */
1621 if (LENGTH (i
) >= len
)
1623 if (! interval_has_some_properties_list (properties
, i
))
1624 return modified
? Qt
: Qnil
;
1626 if (LENGTH (i
) == len
)
1628 remove_properties (Qnil
, properties
, i
, object
);
1629 if (BUFFERP (object
))
1630 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1631 XINT (end
) - XINT (start
));
1635 /* i has the properties, and goes past the change limit */
1637 i
= split_interval_left (i
, len
);
1638 copy_properties (unchanged
, i
);
1639 remove_properties (Qnil
, properties
, i
, object
);
1640 if (BUFFERP (object
))
1641 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1642 XINT (end
) - XINT (start
));
1647 modified
+= remove_properties (Qnil
, properties
, i
, object
);
1648 i
= next_interval (i
);
1652 DEFUN ("text-property-any", Ftext_property_any
,
1653 Stext_property_any
, 4, 5, 0,
1654 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1655 If so, return the position of the first character whose property PROPERTY
1656 is `eq' to VALUE. Otherwise return nil.
1657 If the optional fifth argument OBJECT is a buffer (or nil, which means
1658 the current buffer), START and END are buffer positions (integers or
1659 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1660 (start
, end
, property
, value
, object
)
1661 Lisp_Object start
, end
, property
, value
, object
;
1663 register INTERVAL i
;
1664 register int e
, pos
;
1667 XSETBUFFER (object
, current_buffer
);
1668 i
= validate_interval_range (object
, &start
, &end
, soft
);
1669 if (NULL_INTERVAL_P (i
))
1670 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1673 while (! NULL_INTERVAL_P (i
))
1675 if (i
->position
>= e
)
1677 if (EQ (textget (i
->plist
, property
), value
))
1680 if (pos
< XINT (start
))
1682 return make_number (pos
);
1684 i
= next_interval (i
);
1689 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1690 Stext_property_not_all
, 4, 5, 0,
1691 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1692 If so, return the position of the first character whose property PROPERTY
1693 is not `eq' to VALUE. Otherwise, return nil.
1694 If the optional fifth argument OBJECT is a buffer (or nil, which means
1695 the current buffer), START and END are buffer positions (integers or
1696 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1697 (start
, end
, property
, value
, object
)
1698 Lisp_Object start
, end
, property
, value
, object
;
1700 register INTERVAL i
;
1704 XSETBUFFER (object
, current_buffer
);
1705 i
= validate_interval_range (object
, &start
, &end
, soft
);
1706 if (NULL_INTERVAL_P (i
))
1707 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1711 while (! NULL_INTERVAL_P (i
))
1713 if (i
->position
>= e
)
1715 if (! EQ (textget (i
->plist
, property
), value
))
1717 if (i
->position
> s
)
1719 return make_number (s
);
1721 i
= next_interval (i
);
1727 /* Return the direction from which the text-property PROP would be
1728 inherited by any new text inserted at POS: 1 if it would be
1729 inherited from the char after POS, -1 if it would be inherited from
1730 the char before POS, and 0 if from neither.
1731 BUFFER can be either a buffer or nil (meaning current buffer). */
1734 text_property_stickiness (prop
, pos
, buffer
)
1735 Lisp_Object prop
, pos
, buffer
;
1737 Lisp_Object prev_pos
, front_sticky
;
1738 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1741 XSETBUFFER (buffer
, current_buffer
);
1743 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1744 /* Consider previous character. */
1746 Lisp_Object rear_non_sticky
;
1748 prev_pos
= make_number (XINT (pos
) - 1);
1749 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1751 if (!NILP (CONSP (rear_non_sticky
)
1752 ? Fmemq (prop
, rear_non_sticky
)
1754 /* PROP is rear-non-sticky. */
1758 /* Consider following character. */
1759 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1761 if (EQ (front_sticky
, Qt
)
1762 || (CONSP (front_sticky
)
1763 && !NILP (Fmemq (prop
, front_sticky
))))
1764 /* PROP is inherited from after. */
1765 is_front_sticky
= 1;
1767 /* Simple cases, where the properties are consistent. */
1768 if (is_rear_sticky
&& !is_front_sticky
)
1770 else if (!is_rear_sticky
&& is_front_sticky
)
1772 else if (!is_rear_sticky
&& !is_front_sticky
)
1775 /* The stickiness properties are inconsistent, so we have to
1776 disambiguate. Basically, rear-sticky wins, _except_ if the
1777 property that would be inherited has a value of nil, in which case
1778 front-sticky wins. */
1779 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1780 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1787 /* I don't think this is the right interface to export; how often do you
1788 want to do something like this, other than when you're copying objects
1791 I think it would be better to have a pair of functions, one which
1792 returns the text properties of a region as a list of ranges and
1793 plists, and another which applies such a list to another object. */
1795 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1796 SRC and DEST may each refer to strings or buffers.
1797 Optional sixth argument PROP causes only that property to be copied.
1798 Properties are copied to DEST as if by `add-text-properties'.
1799 Return t if any property value actually changed, nil otherwise. */
1801 /* Note this can GC when DEST is a buffer. */
1804 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1805 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1811 int s
, e
, e2
, p
, len
, modified
= 0;
1812 struct gcpro gcpro1
, gcpro2
;
1814 i
= validate_interval_range (src
, &start
, &end
, soft
);
1815 if (NULL_INTERVAL_P (i
))
1818 CHECK_NUMBER_COERCE_MARKER (pos
);
1820 Lisp_Object dest_start
, dest_end
;
1823 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1824 /* Apply this to a copy of pos; it will try to increment its arguments,
1825 which we don't want. */
1826 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1837 e2
= i
->position
+ LENGTH (i
);
1844 while (! NILP (plist
))
1846 if (EQ (Fcar (plist
), prop
))
1848 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1851 plist
= Fcdr (Fcdr (plist
));
1855 /* Must defer modifications to the interval tree in case src
1856 and dest refer to the same string or buffer. */
1857 stuff
= Fcons (Fcons (make_number (p
),
1858 Fcons (make_number (p
+ len
),
1859 Fcons (plist
, Qnil
))),
1863 i
= next_interval (i
);
1864 if (NULL_INTERVAL_P (i
))
1871 GCPRO2 (stuff
, dest
);
1873 while (! NILP (stuff
))
1876 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1877 Fcar (Fcdr (Fcdr (res
))), dest
);
1880 stuff
= Fcdr (stuff
);
1885 return modified
? Qt
: Qnil
;
1889 /* Return a list representing the text properties of OBJECT between
1890 START and END. if PROP is non-nil, report only on that property.
1891 Each result list element has the form (S E PLIST), where S and E
1892 are positions in OBJECT and PLIST is a property list containing the
1893 text properties of OBJECT between S and E. Value is nil if OBJECT
1894 doesn't contain text properties between START and END. */
1897 text_property_list (object
, start
, end
, prop
)
1898 Lisp_Object object
, start
, end
, prop
;
1905 i
= validate_interval_range (object
, &start
, &end
, soft
);
1906 if (!NULL_INTERVAL_P (i
))
1908 int s
= XINT (start
);
1913 int interval_end
, len
;
1916 interval_end
= i
->position
+ LENGTH (i
);
1917 if (interval_end
> e
)
1919 len
= interval_end
- s
;
1924 for (; !NILP (plist
); plist
= Fcdr (Fcdr (plist
)))
1925 if (EQ (Fcar (plist
), prop
))
1927 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1932 result
= Fcons (Fcons (make_number (s
),
1933 Fcons (make_number (s
+ len
),
1934 Fcons (plist
, Qnil
))),
1937 i
= next_interval (i
);
1938 if (NULL_INTERVAL_P (i
))
1948 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1949 (START END PLIST), where START and END are positions and PLIST is a
1950 property list containing the text properties to add. Adjust START
1951 and END positions by DELTA before adding properties. Value is
1952 non-zero if OBJECT was modified. */
1955 add_text_properties_from_list (object
, list
, delta
)
1956 Lisp_Object object
, list
, delta
;
1958 struct gcpro gcpro1
, gcpro2
;
1961 GCPRO2 (list
, object
);
1963 for (; CONSP (list
); list
= XCDR (list
))
1965 Lisp_Object item
, start
, end
, plist
, tem
;
1968 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1969 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1970 plist
= XCAR (XCDR (XCDR (item
)));
1972 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1983 /* Modify end-points of ranges in LIST destructively. LIST is a list
1984 as returned from text_property_list. Change end-points equal to
1985 OLD_END to NEW_END. */
1988 extend_property_ranges (list
, old_end
, new_end
)
1989 Lisp_Object list
, old_end
, new_end
;
1991 for (; CONSP (list
); list
= XCDR (list
))
1993 Lisp_Object item
, end
;
1996 end
= XCAR (XCDR (item
));
1998 if (EQ (end
, old_end
))
1999 XSETCAR (XCDR (item
), new_end
);
2005 /* Call the modification hook functions in LIST, each with START and END. */
2008 call_mod_hooks (list
, start
, end
)
2009 Lisp_Object list
, start
, end
;
2011 struct gcpro gcpro1
;
2013 while (!NILP (list
))
2015 call2 (Fcar (list
), start
, end
);
2021 /* Check for read-only intervals between character positions START ... END,
2022 in BUF, and signal an error if we find one.
2024 Then check for any modification hooks in the range.
2025 Create a list of all these hooks in lexicographic order,
2026 eliminating consecutive extra copies of the same hook. Then call
2027 those hooks in order, with START and END - 1 as arguments. */
2030 verify_interval_modification (buf
, start
, end
)
2034 register INTERVAL intervals
= BUF_INTERVALS (buf
);
2035 register INTERVAL i
;
2037 register Lisp_Object prev_mod_hooks
;
2038 Lisp_Object mod_hooks
;
2039 struct gcpro gcpro1
;
2042 prev_mod_hooks
= Qnil
;
2045 interval_insert_behind_hooks
= Qnil
;
2046 interval_insert_in_front_hooks
= Qnil
;
2048 if (NULL_INTERVAL_P (intervals
))
2058 /* For an insert operation, check the two chars around the position. */
2061 INTERVAL prev
= NULL
;
2062 Lisp_Object before
, after
;
2064 /* Set I to the interval containing the char after START,
2065 and PREV to the interval containing the char before START.
2066 Either one may be null. They may be equal. */
2067 i
= find_interval (intervals
, start
);
2069 if (start
== BUF_BEGV (buf
))
2071 else if (i
->position
== start
)
2072 prev
= previous_interval (i
);
2073 else if (i
->position
< start
)
2075 if (start
== BUF_ZV (buf
))
2078 /* If Vinhibit_read_only is set and is not a list, we can
2079 skip the read_only checks. */
2080 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2082 /* If I and PREV differ we need to check for the read-only
2083 property together with its stickiness. If either I or
2084 PREV are 0, this check is all we need.
2085 We have to take special care, since read-only may be
2086 indirectly defined via the category property. */
2089 if (! NULL_INTERVAL_P (i
))
2091 after
= textget (i
->plist
, Qread_only
);
2093 /* If interval I is read-only and read-only is
2094 front-sticky, inhibit insertion.
2095 Check for read-only as well as category. */
2097 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2101 tem
= textget (i
->plist
, Qfront_sticky
);
2102 if (TMEM (Qread_only
, tem
)
2103 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2104 && TMEM (Qcategory
, tem
)))
2105 text_read_only (after
);
2109 if (! NULL_INTERVAL_P (prev
))
2111 before
= textget (prev
->plist
, Qread_only
);
2113 /* If interval PREV is read-only and read-only isn't
2114 rear-nonsticky, inhibit insertion.
2115 Check for read-only as well as category. */
2117 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2121 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2122 if (! TMEM (Qread_only
, tem
)
2123 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2124 || ! TMEM (Qcategory
, tem
)))
2125 text_read_only (before
);
2129 else if (! NULL_INTERVAL_P (i
))
2131 after
= textget (i
->plist
, Qread_only
);
2133 /* If interval I is read-only and read-only is
2134 front-sticky, inhibit insertion.
2135 Check for read-only as well as category. */
2136 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2140 tem
= textget (i
->plist
, Qfront_sticky
);
2141 if (TMEM (Qread_only
, tem
)
2142 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2143 && TMEM (Qcategory
, tem
)))
2144 text_read_only (after
);
2146 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2147 if (! TMEM (Qread_only
, tem
)
2148 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2149 || ! TMEM (Qcategory
, tem
)))
2150 text_read_only (after
);
2155 /* Run both insert hooks (just once if they're the same). */
2156 if (!NULL_INTERVAL_P (prev
))
2157 interval_insert_behind_hooks
2158 = textget (prev
->plist
, Qinsert_behind_hooks
);
2159 if (!NULL_INTERVAL_P (i
))
2160 interval_insert_in_front_hooks
2161 = textget (i
->plist
, Qinsert_in_front_hooks
);
2165 /* Loop over intervals on or next to START...END,
2166 collecting their hooks. */
2168 i
= find_interval (intervals
, start
);
2171 if (! INTERVAL_WRITABLE_P (i
))
2172 text_read_only (textget (i
->plist
, Qread_only
));
2174 if (!inhibit_modification_hooks
)
2176 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2177 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2179 hooks
= Fcons (mod_hooks
, hooks
);
2180 prev_mod_hooks
= mod_hooks
;
2184 i
= next_interval (i
);
2186 /* Keep going thru the interval containing the char before END. */
2187 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2189 if (!inhibit_modification_hooks
)
2192 hooks
= Fnreverse (hooks
);
2193 while (! EQ (hooks
, Qnil
))
2195 call_mod_hooks (Fcar (hooks
), make_number (start
),
2197 hooks
= Fcdr (hooks
);
2204 /* Run the interval hooks for an insertion on character range START ... END.
2205 verify_interval_modification chose which hooks to run;
2206 this function is called after the insertion happens
2207 so it can indicate the range of inserted text. */
2210 report_interval_modification (start
, end
)
2211 Lisp_Object start
, end
;
2213 if (! NILP (interval_insert_behind_hooks
))
2214 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2215 if (! NILP (interval_insert_in_front_hooks
)
2216 && ! EQ (interval_insert_in_front_hooks
,
2217 interval_insert_behind_hooks
))
2218 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2224 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
2225 doc
: /* Property-list used as default values.
2226 The value of a property in this list is seen as the value for every
2227 character that does not have its own value for that property. */);
2228 Vdefault_text_properties
= Qnil
;
2230 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist
,
2231 doc
: /* Alist of alternative properties for properties without a value.
2232 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2233 If a piece of text has no direct value for a particular property, then
2234 this alist is consulted. If that property appears in the alist, then
2235 the first non-nil value from the associated alternative properties is
2237 Vchar_property_alias_alist
= Qnil
;
2239 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
2240 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2241 This also inhibits the use of the `intangible' text property. */);
2242 Vinhibit_point_motion_hooks
= Qnil
;
2244 DEFVAR_LISP ("text-property-default-nonsticky",
2245 &Vtext_property_default_nonsticky
,
2246 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2247 Each element has the form (PROPERTY . NONSTICKINESS).
2249 If a character in a buffer has PROPERTY, new text inserted adjacent to
2250 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2251 inherits it if NONSTICKINESS is nil. The front-sticky and
2252 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2253 Vtext_property_default_nonsticky
= Qnil
;
2255 staticpro (&interval_insert_behind_hooks
);
2256 staticpro (&interval_insert_in_front_hooks
);
2257 interval_insert_behind_hooks
= Qnil
;
2258 interval_insert_in_front_hooks
= Qnil
;
2261 /* Common attributes one might give text */
2263 staticpro (&Qforeground
);
2264 Qforeground
= intern ("foreground");
2265 staticpro (&Qbackground
);
2266 Qbackground
= intern ("background");
2268 Qfont
= intern ("font");
2269 staticpro (&Qstipple
);
2270 Qstipple
= intern ("stipple");
2271 staticpro (&Qunderline
);
2272 Qunderline
= intern ("underline");
2273 staticpro (&Qread_only
);
2274 Qread_only
= intern ("read-only");
2275 staticpro (&Qinvisible
);
2276 Qinvisible
= intern ("invisible");
2277 staticpro (&Qintangible
);
2278 Qintangible
= intern ("intangible");
2279 staticpro (&Qcategory
);
2280 Qcategory
= intern ("category");
2281 staticpro (&Qlocal_map
);
2282 Qlocal_map
= intern ("local-map");
2283 staticpro (&Qfront_sticky
);
2284 Qfront_sticky
= intern ("front-sticky");
2285 staticpro (&Qrear_nonsticky
);
2286 Qrear_nonsticky
= intern ("rear-nonsticky");
2287 staticpro (&Qmouse_face
);
2288 Qmouse_face
= intern ("mouse-face");
2290 /* Properties that text might use to specify certain actions */
2292 staticpro (&Qmouse_left
);
2293 Qmouse_left
= intern ("mouse-left");
2294 staticpro (&Qmouse_entered
);
2295 Qmouse_entered
= intern ("mouse-entered");
2296 staticpro (&Qpoint_left
);
2297 Qpoint_left
= intern ("point-left");
2298 staticpro (&Qpoint_entered
);
2299 Qpoint_entered
= intern ("point-entered");
2301 defsubr (&Stext_properties_at
);
2302 defsubr (&Sget_text_property
);
2303 defsubr (&Sget_char_property
);
2304 defsubr (&Sget_char_property_and_overlay
);
2305 defsubr (&Snext_char_property_change
);
2306 defsubr (&Sprevious_char_property_change
);
2307 defsubr (&Snext_single_char_property_change
);
2308 defsubr (&Sprevious_single_char_property_change
);
2309 defsubr (&Snext_property_change
);
2310 defsubr (&Snext_single_property_change
);
2311 defsubr (&Sprevious_property_change
);
2312 defsubr (&Sprevious_single_property_change
);
2313 defsubr (&Sadd_text_properties
);
2314 defsubr (&Sput_text_property
);
2315 defsubr (&Sset_text_properties
);
2316 defsubr (&Sremove_text_properties
);
2317 defsubr (&Sremove_list_of_text_properties
);
2318 defsubr (&Stext_property_any
);
2319 defsubr (&Stext_property_not_all
);
2320 /* defsubr (&Serase_text_properties); */
2321 /* defsubr (&Scopy_text_properties); */
2324 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2325 (do not change this comment) */