1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008 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 3 of the License, or
10 (at your option) any later version.
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. If not, see <http://www.gnu.org/licenses/>. */
22 #include "intervals.h"
27 #define NULL (void *)0
30 /* Test for membership, allowing for t (actually any non-cons) to mean the
33 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
36 /* NOTES: previous- and next- property change will have to skip
37 zero-length intervals if they are implemented. This could be done
38 inside next_interval and previous_interval.
40 set_properties needs to deal with the interval property cache.
42 It is assumed that for any interval plist, a property appears
43 only once on the list. Although some code i.e., remove_properties,
44 handles the more general case, the uniqueness of properties is
45 necessary for the system to remain consistent. This requirement
46 is enforced by the subrs installing properties onto the intervals. */
50 Lisp_Object Qmouse_left
;
51 Lisp_Object Qmouse_entered
;
52 Lisp_Object Qpoint_left
;
53 Lisp_Object Qpoint_entered
;
54 Lisp_Object Qcategory
;
55 Lisp_Object Qlocal_map
;
57 /* Visual properties text (including strings) may have. */
58 Lisp_Object Qforeground
, Qbackground
, Qfont
, Qunderline
, Qstipple
;
59 Lisp_Object Qinvisible
, Qread_only
, Qintangible
, Qmouse_face
;
61 /* Sticky properties */
62 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
64 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
65 the o1's cdr. Otherwise, return zero. This is handy for
67 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
69 Lisp_Object Vinhibit_point_motion_hooks
;
70 Lisp_Object Vdefault_text_properties
;
71 Lisp_Object Vchar_property_alias_alist
;
72 Lisp_Object Vtext_property_default_nonsticky
;
74 /* verify_interval_modification saves insertion hooks here
75 to be run later by report_interval_modification. */
76 Lisp_Object interval_insert_behind_hooks
;
77 Lisp_Object interval_insert_in_front_hooks
;
79 static void text_read_only
P_ ((Lisp_Object
)) NO_RETURN
;
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 if (STRINGP (propval
))
90 xsignal1 (Qtext_read_only
, propval
);
92 xsignal0 (Qtext_read_only
);
97 /* Extract the interval at the position pointed to by BEGIN from
98 OBJECT, a string or buffer. Additionally, check that the positions
99 pointed to by BEGIN and END are within the bounds of OBJECT, and
100 reverse them if *BEGIN is greater than *END. The objects pointed
101 to by BEGIN and END may be integers or markers; if the latter, they
102 are coerced to integers.
104 When OBJECT is a string, we increment *BEGIN and *END
105 to make them origin-one.
107 Note that buffer points don't correspond to interval indices.
108 For example, point-max is 1 greater than the index of the last
109 character. This difference is handled in the caller, which uses
110 the validated points to determine a length, and operates on that.
111 Exceptions are Ftext_properties_at, Fnext_property_change, and
112 Fprevious_property_change which call this function with BEGIN == END.
113 Handle this case specially.
115 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
116 create an interval tree for OBJECT if one doesn't exist, provided
117 the object actually contains text. In the current design, if there
118 is no text, there can be no text properties. */
124 validate_interval_range (object
, begin
, end
, force
)
125 Lisp_Object object
, *begin
, *end
;
131 CHECK_STRING_OR_BUFFER (object
);
132 CHECK_NUMBER_COERCE_MARKER (*begin
);
133 CHECK_NUMBER_COERCE_MARKER (*end
);
135 /* If we are asked for a point, but from a subr which operates
136 on a range, then return nothing. */
137 if (EQ (*begin
, *end
) && begin
!= end
)
138 return NULL_INTERVAL
;
140 if (XINT (*begin
) > XINT (*end
))
148 if (BUFFERP (object
))
150 register struct buffer
*b
= XBUFFER (object
);
152 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
153 && XINT (*end
) <= BUF_ZV (b
)))
154 args_out_of_range (*begin
, *end
);
155 i
= BUF_INTERVALS (b
);
157 /* If there's no text, there are no properties. */
158 if (BUF_BEGV (b
) == BUF_ZV (b
))
159 return NULL_INTERVAL
;
161 searchpos
= XINT (*begin
);
165 int len
= SCHARS (object
);
167 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
168 && XINT (*end
) <= len
))
169 args_out_of_range (*begin
, *end
);
170 XSETFASTINT (*begin
, XFASTINT (*begin
));
172 XSETFASTINT (*end
, XFASTINT (*end
));
173 i
= STRING_INTERVALS (object
);
176 return NULL_INTERVAL
;
178 searchpos
= XINT (*begin
);
181 if (NULL_INTERVAL_P (i
))
182 return (force
? create_root_interval (object
) : i
);
184 return find_interval (i
, searchpos
);
187 /* Validate LIST as a property list. If LIST is not a list, then
188 make one consisting of (LIST nil). Otherwise, verify that LIST
189 is even numbered and thus suitable as a plist. */
192 validate_plist (list
)
201 register Lisp_Object tail
;
202 for (i
= 0, tail
= list
; CONSP (tail
); i
++)
208 error ("Odd length text property list");
212 return Fcons (list
, Fcons (Qnil
, Qnil
));
215 /* Return nonzero if interval I has all the properties,
216 with the same values, of list PLIST. */
219 interval_has_all_properties (plist
, i
)
223 register Lisp_Object tail1
, tail2
, sym1
;
226 /* Go through each element of PLIST. */
227 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
232 /* Go through I's plist, looking for sym1 */
233 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
234 if (EQ (sym1
, XCAR (tail2
)))
236 /* Found the same property on both lists. If the
237 values are unequal, return zero. */
238 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
241 /* Property has same value on both lists; go to next one. */
253 /* Return nonzero if the plist of interval I has any of the
254 properties of PLIST, regardless of their values. */
257 interval_has_some_properties (plist
, i
)
261 register Lisp_Object tail1
, tail2
, sym
;
263 /* Go through each element of PLIST. */
264 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
268 /* Go through i's plist, looking for tail1 */
269 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
270 if (EQ (sym
, XCAR (tail2
)))
277 /* Return nonzero if the plist of interval I has any of the
278 property names in LIST, regardless of their values. */
281 interval_has_some_properties_list (list
, i
)
285 register Lisp_Object tail1
, tail2
, sym
;
287 /* Go through each element of LIST. */
288 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
292 /* Go through i's plist, looking for tail1 */
293 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
294 if (EQ (sym
, XCAR (tail2
)))
301 /* Changing the plists of individual intervals. */
303 /* Return the value of PROP in property-list PLIST, or Qunbound if it
306 property_value (plist
, prop
)
307 Lisp_Object plist
, prop
;
311 while (PLIST_ELT_P (plist
, value
))
312 if (EQ (XCAR (plist
), prop
))
315 plist
= XCDR (value
);
320 /* Set the properties of INTERVAL to PROPERTIES,
321 and record undo info for the previous values.
322 OBJECT is the string or buffer that INTERVAL belongs to. */
325 set_properties (properties
, interval
, object
)
326 Lisp_Object properties
, object
;
329 Lisp_Object sym
, value
;
331 if (BUFFERP (object
))
333 /* For each property in the old plist which is missing from PROPERTIES,
334 or has a different value in PROPERTIES, make an undo record. */
335 for (sym
= interval
->plist
;
336 PLIST_ELT_P (sym
, value
);
338 if (! EQ (property_value (properties
, XCAR (sym
)),
341 record_property_change (interval
->position
, LENGTH (interval
),
342 XCAR (sym
), XCAR (value
),
346 /* For each new property that has no value at all in the old plist,
347 make an undo record binding it to nil, so it will be removed. */
348 for (sym
= properties
;
349 PLIST_ELT_P (sym
, value
);
351 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
353 record_property_change (interval
->position
, LENGTH (interval
),
359 /* Store new properties. */
360 interval
->plist
= Fcopy_sequence (properties
);
363 /* Add the properties of PLIST to the interval I, or set
364 the value of I's property to the value of the property on PLIST
365 if they are different.
367 OBJECT should be the string or buffer the interval is in.
369 Return nonzero if this changes I (i.e., if any members of PLIST
370 are actually added to I's plist) */
373 add_properties (plist
, i
, object
)
378 Lisp_Object tail1
, tail2
, sym1
, val1
;
379 register int changed
= 0;
381 struct gcpro gcpro1
, gcpro2
, gcpro3
;
386 /* No need to protect OBJECT, because we can GC only in the case
387 where it is a buffer, and live buffers are always protected.
388 I and its plist are also protected, via OBJECT. */
389 GCPRO3 (tail1
, sym1
, val1
);
391 /* Go through each element of PLIST. */
392 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
395 val1
= Fcar (XCDR (tail1
));
398 /* Go through I's plist, looking for sym1 */
399 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
400 if (EQ (sym1
, XCAR (tail2
)))
402 /* No need to gcpro, because tail2 protects this
403 and it must be a cons cell (we get an error otherwise). */
404 register Lisp_Object this_cdr
;
406 this_cdr
= XCDR (tail2
);
407 /* Found the property. Now check its value. */
410 /* The properties have the same value on both lists.
411 Continue to the next property. */
412 if (EQ (val1
, Fcar (this_cdr
)))
415 /* Record this change in the buffer, for undo purposes. */
416 if (BUFFERP (object
))
418 record_property_change (i
->position
, LENGTH (i
),
419 sym1
, Fcar (this_cdr
), object
);
422 /* I's property has a different value -- change it */
423 Fsetcar (this_cdr
, val1
);
430 /* Record this change in the buffer, for undo purposes. */
431 if (BUFFERP (object
))
433 record_property_change (i
->position
, LENGTH (i
),
436 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
446 /* For any members of PLIST, or LIST,
447 which are properties of I, remove them from I's plist.
448 (If PLIST is non-nil, use that, otherwise use LIST.)
449 OBJECT is the string or buffer containing I. */
452 remove_properties (plist
, list
, i
, object
)
453 Lisp_Object plist
, list
;
457 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
458 register int changed
= 0;
460 /* Nonzero means tail1 is a plist, otherwise it is a list. */
463 current_plist
= i
->plist
;
466 tail1
= plist
, use_plist
= 1;
468 tail1
= list
, use_plist
= 0;
470 /* Go through each element of LIST or PLIST. */
471 while (CONSP (tail1
))
475 /* First, remove the symbol if it's at the head of the list */
476 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
478 if (BUFFERP (object
))
479 record_property_change (i
->position
, LENGTH (i
),
480 sym
, XCAR (XCDR (current_plist
)),
483 current_plist
= XCDR (XCDR (current_plist
));
487 /* Go through I's plist, looking for SYM. */
488 tail2
= current_plist
;
489 while (! NILP (tail2
))
491 register Lisp_Object
this;
492 this = XCDR (XCDR (tail2
));
493 if (CONSP (this) && EQ (sym
, XCAR (this)))
495 if (BUFFERP (object
))
496 record_property_change (i
->position
, LENGTH (i
),
497 sym
, XCAR (XCDR (this)), object
);
499 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
505 /* Advance thru TAIL1 one way or the other. */
506 tail1
= XCDR (tail1
);
507 if (use_plist
&& CONSP (tail1
))
508 tail1
= XCDR (tail1
);
512 i
->plist
= current_plist
;
517 /* Remove all properties from interval I. Return non-zero
518 if this changes the interval. */
532 /* Returns the interval of POSITION in OBJECT.
533 POSITION is BEG-based. */
536 interval_of (position
, object
)
544 XSETBUFFER (object
, current_buffer
);
545 else if (EQ (object
, Qt
))
546 return NULL_INTERVAL
;
548 CHECK_STRING_OR_BUFFER (object
);
550 if (BUFFERP (object
))
552 register struct buffer
*b
= XBUFFER (object
);
556 i
= BUF_INTERVALS (b
);
561 end
= SCHARS (object
);
562 i
= STRING_INTERVALS (object
);
565 if (!(beg
<= position
&& position
<= end
))
566 args_out_of_range (make_number (position
), make_number (position
));
567 if (beg
== end
|| NULL_INTERVAL_P (i
))
568 return NULL_INTERVAL
;
570 return find_interval (i
, position
);
573 DEFUN ("text-properties-at", Ftext_properties_at
,
574 Stext_properties_at
, 1, 2, 0,
575 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
576 If the optional second argument OBJECT is a buffer (or nil, which means
577 the current buffer), POSITION is a buffer position (integer or marker).
578 If OBJECT is a string, POSITION is a 0-based index into it.
579 If POSITION is at the end of OBJECT, the value is nil. */)
581 Lisp_Object position
, object
;
586 XSETBUFFER (object
, current_buffer
);
588 i
= validate_interval_range (object
, &position
, &position
, soft
);
589 if (NULL_INTERVAL_P (i
))
591 /* If POSITION is at the end of the interval,
592 it means it's the end of OBJECT.
593 There are no properties at the very end,
594 since no character follows. */
595 if (XINT (position
) == LENGTH (i
) + i
->position
)
601 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
602 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
603 OBJECT is optional and defaults to the current buffer.
604 If POSITION is at the end of OBJECT, the value is nil. */)
605 (position
, prop
, object
)
606 Lisp_Object position
, object
;
609 return textget (Ftext_properties_at (position
, object
), prop
);
612 /* Return the value of char's property PROP, in OBJECT at POSITION.
613 OBJECT is optional and defaults to the current buffer.
614 If OVERLAY is non-0, then in the case that the returned property is from
615 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
616 returned in *OVERLAY.
617 If POSITION is at the end of OBJECT, the value is nil.
618 If OBJECT is a buffer, then overlay properties are considered as well as
620 If OBJECT is a window, then that window's buffer is used, but
621 window-specific overlays are considered only if they are associated
624 get_char_property_and_overlay (position
, prop
, object
, overlay
)
625 Lisp_Object position
, object
;
626 register Lisp_Object prop
;
627 Lisp_Object
*overlay
;
629 struct window
*w
= 0;
631 CHECK_NUMBER_COERCE_MARKER (position
);
634 XSETBUFFER (object
, current_buffer
);
636 if (WINDOWP (object
))
638 w
= XWINDOW (object
);
641 if (BUFFERP (object
))
644 Lisp_Object
*overlay_vec
;
645 struct buffer
*obuf
= current_buffer
;
647 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
648 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
649 xsignal1 (Qargs_out_of_range
, position
);
651 set_buffer_temp (XBUFFER (object
));
653 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
654 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
656 set_buffer_temp (obuf
);
658 /* Now check the overlays in order of decreasing priority. */
659 while (--noverlays
>= 0)
661 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
665 /* Return the overlay we got the property from. */
666 *overlay
= overlay_vec
[noverlays
];
673 /* Indicate that the return value is not from an overlay. */
676 /* Not a buffer, or no appropriate overlay, so fall through to the
678 return Fget_text_property (position
, prop
, object
);
681 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
682 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
683 Both overlay properties and text properties are checked.
684 OBJECT is optional and defaults to the current buffer.
685 If POSITION is at the end of OBJECT, the value is nil.
686 If OBJECT is a buffer, then overlay properties are considered as well as
688 If OBJECT is a window, then that window's buffer is used, but window-specific
689 overlays are considered only if they are associated with OBJECT. */)
690 (position
, prop
, object
)
691 Lisp_Object position
, object
;
692 register Lisp_Object prop
;
694 return get_char_property_and_overlay (position
, prop
, object
, 0);
697 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
698 Sget_char_property_and_overlay
, 2, 3, 0,
699 doc
: /* Like `get-char-property', but with extra overlay information.
700 The value is a cons cell. Its car is the return value of `get-char-property'
701 with the same arguments--that is, the value of POSITION's property
702 PROP in OBJECT. Its cdr is the overlay in which the property was
703 found, or nil, if it was found as a text property or not found at all.
705 OBJECT is optional and defaults to the current buffer. OBJECT may be
706 a string, a buffer or a window. For strings, the cdr of the return
707 value is always nil, since strings do not have overlays. If OBJECT is
708 a window, then that window's buffer is used, but window-specific
709 overlays are considered only if they are associated with OBJECT. If
710 POSITION is at the end of OBJECT, both car and cdr are nil. */)
711 (position
, prop
, object
)
712 Lisp_Object position
, object
;
713 register Lisp_Object prop
;
717 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
718 return Fcons (val
, overlay
);
722 DEFUN ("next-char-property-change", Fnext_char_property_change
,
723 Snext_char_property_change
, 1, 2, 0,
724 doc
: /* Return the position of next text property or overlay change.
725 This scans characters forward in the current buffer from POSITION till
726 it finds a change in some text property, or the beginning or end of an
727 overlay, and returns the position of that.
728 If none is found up to (point-max), the function returns (point-max).
730 If the optional second argument LIMIT is non-nil, don't search
731 past position LIMIT; return LIMIT if nothing is found before LIMIT.
732 LIMIT is a no-op if it is greater than (point-max). */)
734 Lisp_Object position
, limit
;
738 temp
= Fnext_overlay_change (position
);
741 CHECK_NUMBER_COERCE_MARKER (limit
);
742 if (XINT (limit
) < XINT (temp
))
745 return Fnext_property_change (position
, Qnil
, temp
);
748 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
749 Sprevious_char_property_change
, 1, 2, 0,
750 doc
: /* Return the position of previous text property or overlay change.
751 Scans characters backward in the current buffer from POSITION till it
752 finds a change in some text property, or the beginning or end of an
753 overlay, and returns the position of that.
754 If none is found since (point-min), the function returns (point-min).
756 If the optional second argument LIMIT is non-nil, don't search
757 past position LIMIT; return LIMIT if nothing is found before LIMIT.
758 LIMIT is a no-op if it is less than (point-min). */)
760 Lisp_Object position
, limit
;
764 temp
= Fprevious_overlay_change (position
);
767 CHECK_NUMBER_COERCE_MARKER (limit
);
768 if (XINT (limit
) > XINT (temp
))
771 return Fprevious_property_change (position
, Qnil
, temp
);
775 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
776 Snext_single_char_property_change
, 2, 4, 0,
777 doc
: /* Return the position of next text property or overlay change for a specific property.
778 Scans characters forward from POSITION till it finds
779 a change in the PROP property, then returns the position of the change.
780 If the optional third argument OBJECT is a buffer (or nil, which means
781 the current buffer), POSITION is a buffer position (integer or marker).
782 If OBJECT is a string, POSITION is a 0-based index into it.
784 In a string, scan runs to the end of the string.
785 In a buffer, it runs to (point-max), and the value cannot exceed that.
787 The property values are compared with `eq'.
788 If the property is constant all the way to the end of OBJECT, return the
789 last valid position in OBJECT.
790 If the optional fourth argument LIMIT is non-nil, don't search
791 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
792 (position
, prop
, object
, limit
)
793 Lisp_Object prop
, position
, object
, limit
;
795 if (STRINGP (object
))
797 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
801 position
= make_number (SCHARS (object
));
804 CHECK_NUMBER (limit
);
811 Lisp_Object initial_value
, value
;
812 int count
= SPECPDL_INDEX ();
815 CHECK_BUFFER (object
);
817 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
819 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
820 Fset_buffer (object
);
823 CHECK_NUMBER_COERCE_MARKER (position
);
825 initial_value
= Fget_char_property (position
, prop
, object
);
828 XSETFASTINT (limit
, ZV
);
830 CHECK_NUMBER_COERCE_MARKER (limit
);
832 if (XFASTINT (position
) >= XFASTINT (limit
))
835 if (XFASTINT (position
) > ZV
)
836 XSETFASTINT (position
, ZV
);
841 position
= Fnext_char_property_change (position
, limit
);
842 if (XFASTINT (position
) >= XFASTINT (limit
))
848 value
= Fget_char_property (position
, prop
, object
);
849 if (!EQ (value
, initial_value
))
853 unbind_to (count
, Qnil
);
859 DEFUN ("previous-single-char-property-change",
860 Fprevious_single_char_property_change
,
861 Sprevious_single_char_property_change
, 2, 4, 0,
862 doc
: /* Return the position of previous text property or overlay change for a specific property.
863 Scans characters backward from POSITION till it finds
864 a change in the PROP property, then returns the position of the change.
865 If the optional third argument OBJECT is a buffer (or nil, which means
866 the current buffer), POSITION is a buffer position (integer or marker).
867 If OBJECT is a string, POSITION is a 0-based index into it.
869 In a string, scan runs to the start of the string.
870 In a buffer, it runs to (point-min), and the value cannot be less than that.
872 The property values are compared with `eq'.
873 If the property is constant all the way to the start of OBJECT, return the
874 first valid position in OBJECT.
875 If the optional fourth argument LIMIT is non-nil, don't search
876 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
877 (position
, prop
, object
, limit
)
878 Lisp_Object prop
, position
, object
, limit
;
880 if (STRINGP (object
))
882 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
886 position
= make_number (SCHARS (object
));
889 CHECK_NUMBER (limit
);
896 int count
= SPECPDL_INDEX ();
899 CHECK_BUFFER (object
);
901 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
903 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
904 Fset_buffer (object
);
907 CHECK_NUMBER_COERCE_MARKER (position
);
910 XSETFASTINT (limit
, BEGV
);
912 CHECK_NUMBER_COERCE_MARKER (limit
);
914 if (XFASTINT (position
) <= XFASTINT (limit
))
917 if (XFASTINT (position
) < BEGV
)
918 XSETFASTINT (position
, BEGV
);
922 Lisp_Object initial_value
923 = Fget_char_property (make_number (XFASTINT (position
) - 1),
928 position
= Fprevious_char_property_change (position
, limit
);
930 if (XFASTINT (position
) <= XFASTINT (limit
))
938 = Fget_char_property (make_number (XFASTINT (position
) - 1),
941 if (!EQ (value
, initial_value
))
947 unbind_to (count
, Qnil
);
953 DEFUN ("next-property-change", Fnext_property_change
,
954 Snext_property_change
, 1, 3, 0,
955 doc
: /* Return the position of next property change.
956 Scans characters forward from POSITION in OBJECT till it finds
957 a change in some text property, then returns the position of the change.
958 If the optional second argument OBJECT is a buffer (or nil, which means
959 the current buffer), POSITION is a buffer position (integer or marker).
960 If OBJECT is a string, POSITION is a 0-based index into it.
961 Return nil if the property is constant all the way to the end of OBJECT.
962 If the value is non-nil, it is a position greater than POSITION, never equal.
964 If the optional third argument LIMIT is non-nil, don't search
965 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
966 (position
, object
, limit
)
967 Lisp_Object position
, object
, limit
;
969 register INTERVAL i
, next
;
972 XSETBUFFER (object
, current_buffer
);
974 if (!NILP (limit
) && !EQ (limit
, Qt
))
975 CHECK_NUMBER_COERCE_MARKER (limit
);
977 i
= validate_interval_range (object
, &position
, &position
, soft
);
979 /* If LIMIT is t, return start of next interval--don't
980 bother checking further intervals. */
983 if (NULL_INTERVAL_P (i
))
986 next
= next_interval (i
);
988 if (NULL_INTERVAL_P (next
))
989 XSETFASTINT (position
, (STRINGP (object
)
991 : BUF_ZV (XBUFFER (object
))));
993 XSETFASTINT (position
, next
->position
);
997 if (NULL_INTERVAL_P (i
))
1000 next
= next_interval (i
);
1002 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
1003 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1004 next
= next_interval (next
);
1006 if (NULL_INTERVAL_P (next
)
1008 >= (INTEGERP (limit
)
1012 : BUF_ZV (XBUFFER (object
))))))
1015 return make_number (next
->position
);
1018 /* Return 1 if there's a change in some property between BEG and END. */
1021 property_change_between_p (beg
, end
)
1024 register INTERVAL i
, next
;
1025 Lisp_Object object
, pos
;
1027 XSETBUFFER (object
, current_buffer
);
1028 XSETFASTINT (pos
, beg
);
1030 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
1031 if (NULL_INTERVAL_P (i
))
1034 next
= next_interval (i
);
1035 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
1037 next
= next_interval (next
);
1038 if (NULL_INTERVAL_P (next
))
1040 if (next
->position
>= end
)
1044 if (NULL_INTERVAL_P (next
))
1050 DEFUN ("next-single-property-change", Fnext_single_property_change
,
1051 Snext_single_property_change
, 2, 4, 0,
1052 doc
: /* Return the position of next property change for a specific property.
1053 Scans characters forward from POSITION till it finds
1054 a change in the PROP property, then returns the position of the change.
1055 If the optional third argument OBJECT is a buffer (or nil, which means
1056 the current buffer), POSITION is a buffer position (integer or marker).
1057 If OBJECT is a string, POSITION is a 0-based index into it.
1058 The property values are compared with `eq'.
1059 Return nil if the property is constant all the way to the end of OBJECT.
1060 If the value is non-nil, it is a position greater than POSITION, never equal.
1062 If the optional fourth argument LIMIT is non-nil, don't search
1063 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1064 (position
, prop
, object
, limit
)
1065 Lisp_Object position
, prop
, object
, limit
;
1067 register INTERVAL i
, next
;
1068 register Lisp_Object here_val
;
1071 XSETBUFFER (object
, current_buffer
);
1074 CHECK_NUMBER_COERCE_MARKER (limit
);
1076 i
= validate_interval_range (object
, &position
, &position
, soft
);
1077 if (NULL_INTERVAL_P (i
))
1080 here_val
= textget (i
->plist
, prop
);
1081 next
= next_interval (i
);
1082 while (! NULL_INTERVAL_P (next
)
1083 && EQ (here_val
, textget (next
->plist
, prop
))
1084 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1085 next
= next_interval (next
);
1087 if (NULL_INTERVAL_P (next
)
1089 >= (INTEGERP (limit
)
1093 : BUF_ZV (XBUFFER (object
))))))
1096 return make_number (next
->position
);
1099 DEFUN ("previous-property-change", Fprevious_property_change
,
1100 Sprevious_property_change
, 1, 3, 0,
1101 doc
: /* Return the position of previous property change.
1102 Scans characters backwards from POSITION in OBJECT till it finds
1103 a change in some text property, then returns the position of the change.
1104 If the optional second argument OBJECT is a buffer (or nil, which means
1105 the current buffer), POSITION is a buffer position (integer or marker).
1106 If OBJECT is a string, POSITION is a 0-based index into it.
1107 Return nil if the property is constant all the way to the start of OBJECT.
1108 If the value is non-nil, it is a position less than POSITION, never equal.
1110 If the optional third argument LIMIT is non-nil, don't search
1111 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1112 (position
, object
, limit
)
1113 Lisp_Object position
, object
, limit
;
1115 register INTERVAL i
, previous
;
1118 XSETBUFFER (object
, current_buffer
);
1121 CHECK_NUMBER_COERCE_MARKER (limit
);
1123 i
= validate_interval_range (object
, &position
, &position
, soft
);
1124 if (NULL_INTERVAL_P (i
))
1127 /* Start with the interval containing the char before point. */
1128 if (i
->position
== XFASTINT (position
))
1129 i
= previous_interval (i
);
1131 previous
= previous_interval (i
);
1132 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1134 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1135 previous
= previous_interval (previous
);
1137 if (NULL_INTERVAL_P (previous
)
1138 || (previous
->position
+ LENGTH (previous
)
1139 <= (INTEGERP (limit
)
1141 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1144 return make_number (previous
->position
+ LENGTH (previous
));
1147 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1148 Sprevious_single_property_change
, 2, 4, 0,
1149 doc
: /* Return the position of previous property change for a specific property.
1150 Scans characters backward from POSITION till it finds
1151 a change in the PROP property, then returns the position of the change.
1152 If the optional third argument OBJECT is a buffer (or nil, which means
1153 the current buffer), POSITION is a buffer position (integer or marker).
1154 If OBJECT is a string, POSITION is a 0-based index into it.
1155 The property values are compared with `eq'.
1156 Return nil if the property is constant all the way to the start of OBJECT.
1157 If the value is non-nil, it is a position less than POSITION, never equal.
1159 If the optional fourth argument LIMIT is non-nil, don't search
1160 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1161 (position
, prop
, object
, limit
)
1162 Lisp_Object position
, prop
, object
, limit
;
1164 register INTERVAL i
, previous
;
1165 register Lisp_Object here_val
;
1168 XSETBUFFER (object
, current_buffer
);
1171 CHECK_NUMBER_COERCE_MARKER (limit
);
1173 i
= validate_interval_range (object
, &position
, &position
, soft
);
1175 /* Start with the interval containing the char before point. */
1176 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1177 i
= previous_interval (i
);
1179 if (NULL_INTERVAL_P (i
))
1182 here_val
= textget (i
->plist
, prop
);
1183 previous
= previous_interval (i
);
1184 while (!NULL_INTERVAL_P (previous
)
1185 && EQ (here_val
, textget (previous
->plist
, prop
))
1187 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1188 previous
= previous_interval (previous
);
1190 if (NULL_INTERVAL_P (previous
)
1191 || (previous
->position
+ LENGTH (previous
)
1192 <= (INTEGERP (limit
)
1194 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1197 return make_number (previous
->position
+ LENGTH (previous
));
1200 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1202 DEFUN ("add-text-properties", Fadd_text_properties
,
1203 Sadd_text_properties
, 3, 4, 0,
1204 doc
: /* Add properties to the text from START to END.
1205 The third argument PROPERTIES is a property list
1206 specifying the property values to add. If the optional fourth argument
1207 OBJECT is a buffer (or nil, which means the current buffer),
1208 START and END are buffer positions (integers or markers).
1209 If OBJECT is a string, START and END are 0-based indices into it.
1210 Return t if any property value actually changed, nil otherwise. */)
1211 (start
, end
, properties
, object
)
1212 Lisp_Object start
, end
, properties
, object
;
1214 register INTERVAL i
, unchanged
;
1215 register int s
, len
, modified
= 0;
1216 struct gcpro gcpro1
;
1218 properties
= validate_plist (properties
);
1219 if (NILP (properties
))
1223 XSETBUFFER (object
, current_buffer
);
1225 i
= validate_interval_range (object
, &start
, &end
, hard
);
1226 if (NULL_INTERVAL_P (i
))
1230 len
= XINT (end
) - s
;
1232 /* No need to protect OBJECT, because we GC only if it's a buffer,
1233 and live buffers are always protected. */
1234 GCPRO1 (properties
);
1236 /* If we're not starting on an interval boundary, we have to
1237 split this interval. */
1238 if (i
->position
!= s
)
1240 /* If this interval already has the properties, we can
1242 if (interval_has_all_properties (properties
, i
))
1244 int got
= (LENGTH (i
) - (s
- i
->position
));
1246 RETURN_UNGCPRO (Qnil
);
1248 i
= next_interval (i
);
1253 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1254 copy_properties (unchanged
, i
);
1258 if (BUFFERP (object
))
1259 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1261 /* We are at the beginning of interval I, with LEN chars to scan. */
1267 if (LENGTH (i
) >= len
)
1269 /* We can UNGCPRO safely here, because there will be just
1270 one more chance to gc, in the next call to add_properties,
1271 and after that we will not need PROPERTIES or OBJECT again. */
1274 if (interval_has_all_properties (properties
, i
))
1276 if (BUFFERP (object
))
1277 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1278 XINT (end
) - XINT (start
));
1280 return modified
? Qt
: Qnil
;
1283 if (LENGTH (i
) == len
)
1285 add_properties (properties
, i
, object
);
1286 if (BUFFERP (object
))
1287 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1288 XINT (end
) - XINT (start
));
1292 /* i doesn't have the properties, and goes past the change limit */
1294 i
= split_interval_left (unchanged
, len
);
1295 copy_properties (unchanged
, i
);
1296 add_properties (properties
, i
, object
);
1297 if (BUFFERP (object
))
1298 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1299 XINT (end
) - XINT (start
));
1304 modified
+= add_properties (properties
, i
, object
);
1305 i
= next_interval (i
);
1309 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1311 DEFUN ("put-text-property", Fput_text_property
,
1312 Sput_text_property
, 4, 5, 0,
1313 doc
: /* Set one property of the text from START to END.
1314 The third and fourth arguments PROPERTY and VALUE
1315 specify the property to add.
1316 If the optional fifth argument OBJECT is a buffer (or nil, which means
1317 the current buffer), START and END are buffer positions (integers or
1318 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1319 (start
, end
, property
, value
, object
)
1320 Lisp_Object start
, end
, property
, value
, object
;
1322 Fadd_text_properties (start
, end
,
1323 Fcons (property
, Fcons (value
, Qnil
)),
1328 DEFUN ("set-text-properties", Fset_text_properties
,
1329 Sset_text_properties
, 3, 4, 0,
1330 doc
: /* Completely replace properties of text from START to END.
1331 The third argument PROPERTIES is the new property list.
1332 If the optional fourth argument OBJECT is a buffer (or nil, which means
1333 the current buffer), START and END are buffer positions (integers or
1334 markers). If OBJECT is a string, START and END are 0-based indices into it.
1335 If PROPERTIES is nil, the effect is to remove all properties from
1336 the designated part of OBJECT. */)
1337 (start
, end
, properties
, object
)
1338 Lisp_Object start
, end
, properties
, object
;
1340 return set_text_properties (start
, end
, properties
, object
, Qt
);
1344 /* Replace properties of text from START to END with new list of
1345 properties PROPERTIES. OBJECT is the buffer or string containing
1346 the text. OBJECT nil means use the current buffer.
1347 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1348 is nil if the function _detected_ that it did not replace any
1349 properties, non-nil otherwise. */
1352 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1353 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1355 register INTERVAL i
;
1356 Lisp_Object ostart
, oend
;
1361 properties
= validate_plist (properties
);
1364 XSETBUFFER (object
, current_buffer
);
1366 /* If we want no properties for a whole string,
1367 get rid of its intervals. */
1368 if (NILP (properties
) && STRINGP (object
)
1369 && XFASTINT (start
) == 0
1370 && XFASTINT (end
) == SCHARS (object
))
1372 if (! STRING_INTERVALS (object
))
1375 STRING_SET_INTERVALS (object
, NULL_INTERVAL
);
1379 i
= validate_interval_range (object
, &start
, &end
, soft
);
1381 if (NULL_INTERVAL_P (i
))
1383 /* If buffer has no properties, and we want none, return now. */
1384 if (NILP (properties
))
1387 /* Restore the original START and END values
1388 because validate_interval_range increments them for strings. */
1392 i
= validate_interval_range (object
, &start
, &end
, hard
);
1393 /* This can return if start == end. */
1394 if (NULL_INTERVAL_P (i
))
1398 if (BUFFERP (object
))
1399 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1401 set_text_properties_1 (start
, end
, properties
, object
, i
);
1403 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1404 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1405 XINT (end
) - XINT (start
));
1409 /* Replace properties of text from START to END with new list of
1410 properties PROPERTIES. BUFFER is the buffer containing
1411 the text. This does not obey any hooks.
1412 You can provide the interval that START is located in as I,
1413 or pass NULL for I and this function will find it.
1414 START and END can be in any order. */
1417 set_text_properties_1 (start
, end
, properties
, buffer
, i
)
1418 Lisp_Object start
, end
, properties
, buffer
;
1421 register INTERVAL prev_changed
= NULL_INTERVAL
;
1422 register int s
, len
;
1426 len
= XINT (end
) - s
;
1436 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1438 if (i
->position
!= s
)
1441 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1443 if (LENGTH (i
) > len
)
1445 copy_properties (unchanged
, i
);
1446 i
= split_interval_left (i
, len
);
1447 set_properties (properties
, i
, buffer
);
1451 set_properties (properties
, i
, buffer
);
1453 if (LENGTH (i
) == len
)
1458 i
= next_interval (i
);
1461 /* We are starting at the beginning of an interval, I */
1467 if (LENGTH (i
) >= len
)
1469 if (LENGTH (i
) > len
)
1470 i
= split_interval_left (i
, len
);
1472 /* We have to call set_properties even if we are going to
1473 merge the intervals, so as to make the undo records
1474 and cause redisplay to happen. */
1475 set_properties (properties
, i
, buffer
);
1476 if (!NULL_INTERVAL_P (prev_changed
))
1477 merge_interval_left (i
);
1483 /* We have to call set_properties even if we are going to
1484 merge the intervals, so as to make the undo records
1485 and cause redisplay to happen. */
1486 set_properties (properties
, i
, buffer
);
1487 if (NULL_INTERVAL_P (prev_changed
))
1490 prev_changed
= i
= merge_interval_left (i
);
1492 i
= next_interval (i
);
1496 DEFUN ("remove-text-properties", Fremove_text_properties
,
1497 Sremove_text_properties
, 3, 4, 0,
1498 doc
: /* Remove some properties from text from START to END.
1499 The third argument PROPERTIES is a property list
1500 whose property names specify the properties to remove.
1501 \(The values stored in PROPERTIES are ignored.)
1502 If the optional fourth argument OBJECT is a buffer (or nil, which means
1503 the current buffer), START and END are buffer positions (integers or
1504 markers). If OBJECT is a string, START and END are 0-based indices into it.
1505 Return t if any property was actually removed, nil otherwise.
1507 Use set-text-properties if you want to remove all text properties. */)
1508 (start
, end
, properties
, object
)
1509 Lisp_Object start
, end
, properties
, object
;
1511 register INTERVAL i
, unchanged
;
1512 register int s
, len
, modified
= 0;
1515 XSETBUFFER (object
, current_buffer
);
1517 i
= validate_interval_range (object
, &start
, &end
, soft
);
1518 if (NULL_INTERVAL_P (i
))
1522 len
= XINT (end
) - s
;
1524 if (i
->position
!= s
)
1526 /* No properties on this first interval -- return if
1527 it covers the entire region. */
1528 if (! interval_has_some_properties (properties
, i
))
1530 int got
= (LENGTH (i
) - (s
- i
->position
));
1534 i
= next_interval (i
);
1536 /* Split away the beginning of this interval; what we don't
1541 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1542 copy_properties (unchanged
, i
);
1546 if (BUFFERP (object
))
1547 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1549 /* We are at the beginning of an interval, with len to scan */
1555 if (LENGTH (i
) >= len
)
1557 if (! interval_has_some_properties (properties
, i
))
1558 return modified
? Qt
: Qnil
;
1560 if (LENGTH (i
) == len
)
1562 remove_properties (properties
, Qnil
, i
, object
);
1563 if (BUFFERP (object
))
1564 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1565 XINT (end
) - XINT (start
));
1569 /* i has the properties, and goes past the change limit */
1571 i
= split_interval_left (i
, len
);
1572 copy_properties (unchanged
, i
);
1573 remove_properties (properties
, Qnil
, i
, object
);
1574 if (BUFFERP (object
))
1575 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1576 XINT (end
) - XINT (start
));
1581 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1582 i
= next_interval (i
);
1586 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1587 Sremove_list_of_text_properties
, 3, 4, 0,
1588 doc
: /* Remove some properties from text from START to END.
1589 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1590 If the optional fourth argument OBJECT is a buffer (or nil, which means
1591 the current buffer), START and END are buffer positions (integers or
1592 markers). If OBJECT is a string, START and END are 0-based indices into it.
1593 Return t if any property was actually removed, nil otherwise. */)
1594 (start
, end
, list_of_properties
, object
)
1595 Lisp_Object start
, end
, list_of_properties
, object
;
1597 register INTERVAL i
, unchanged
;
1598 register int s
, len
, modified
= 0;
1599 Lisp_Object properties
;
1600 properties
= list_of_properties
;
1603 XSETBUFFER (object
, current_buffer
);
1605 i
= validate_interval_range (object
, &start
, &end
, soft
);
1606 if (NULL_INTERVAL_P (i
))
1610 len
= XINT (end
) - s
;
1612 if (i
->position
!= s
)
1614 /* No properties on this first interval -- return if
1615 it covers the entire region. */
1616 if (! interval_has_some_properties_list (properties
, i
))
1618 int got
= (LENGTH (i
) - (s
- i
->position
));
1622 i
= next_interval (i
);
1624 /* Split away the beginning of this interval; what we don't
1629 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1630 copy_properties (unchanged
, i
);
1634 /* We are at the beginning of an interval, with len to scan.
1635 The flag `modified' records if changes have been made.
1636 When object is a buffer, we must call modify_region before changes are
1637 made and signal_after_change when we are done.
1638 We call modify_region before calling remove_properties if modified == 0,
1639 and we call signal_after_change before returning if modified != 0. */
1645 if (LENGTH (i
) >= len
)
1647 if (! interval_has_some_properties_list (properties
, i
))
1650 if (BUFFERP (object
))
1651 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1652 XINT (end
) - XINT (start
));
1658 if (LENGTH (i
) == len
)
1660 if (!modified
&& BUFFERP (object
))
1661 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1662 remove_properties (Qnil
, properties
, i
, object
);
1663 if (BUFFERP (object
))
1664 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1665 XINT (end
) - XINT (start
));
1669 /* i has the properties, and goes past the change limit */
1671 i
= split_interval_left (i
, len
);
1672 copy_properties (unchanged
, i
);
1673 if (!modified
&& BUFFERP (object
))
1674 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1675 remove_properties (Qnil
, properties
, i
, object
);
1676 if (BUFFERP (object
))
1677 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1678 XINT (end
) - XINT (start
));
1682 if (interval_has_some_properties_list (properties
, i
))
1684 if (!modified
&& BUFFERP (object
))
1685 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1686 remove_properties (Qnil
, properties
, i
, object
);
1690 i
= next_interval (i
);
1694 DEFUN ("text-property-any", Ftext_property_any
,
1695 Stext_property_any
, 4, 5, 0,
1696 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1697 If so, return the position of the first character whose property PROPERTY
1698 is `eq' to VALUE. Otherwise return nil.
1699 If the optional fifth argument OBJECT is a buffer (or nil, which means
1700 the current buffer), START and END are buffer positions (integers or
1701 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1702 (start
, end
, property
, value
, object
)
1703 Lisp_Object start
, end
, property
, value
, object
;
1705 register INTERVAL i
;
1706 register int e
, pos
;
1709 XSETBUFFER (object
, current_buffer
);
1710 i
= validate_interval_range (object
, &start
, &end
, soft
);
1711 if (NULL_INTERVAL_P (i
))
1712 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1715 while (! NULL_INTERVAL_P (i
))
1717 if (i
->position
>= e
)
1719 if (EQ (textget (i
->plist
, property
), value
))
1722 if (pos
< XINT (start
))
1724 return make_number (pos
);
1726 i
= next_interval (i
);
1731 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1732 Stext_property_not_all
, 4, 5, 0,
1733 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1734 If so, return the position of the first character whose property PROPERTY
1735 is not `eq' to VALUE. Otherwise, return nil.
1736 If the optional fifth argument OBJECT is a buffer (or nil, which means
1737 the current buffer), START and END are buffer positions (integers or
1738 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1739 (start
, end
, property
, value
, object
)
1740 Lisp_Object start
, end
, property
, value
, object
;
1742 register INTERVAL i
;
1746 XSETBUFFER (object
, current_buffer
);
1747 i
= validate_interval_range (object
, &start
, &end
, soft
);
1748 if (NULL_INTERVAL_P (i
))
1749 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1753 while (! NULL_INTERVAL_P (i
))
1755 if (i
->position
>= e
)
1757 if (! EQ (textget (i
->plist
, property
), value
))
1759 if (i
->position
> s
)
1761 return make_number (s
);
1763 i
= next_interval (i
);
1769 /* Return the direction from which the text-property PROP would be
1770 inherited by any new text inserted at POS: 1 if it would be
1771 inherited from the char after POS, -1 if it would be inherited from
1772 the char before POS, and 0 if from neither.
1773 BUFFER can be either a buffer or nil (meaning current buffer). */
1776 text_property_stickiness (prop
, pos
, buffer
)
1777 Lisp_Object prop
, pos
, buffer
;
1779 Lisp_Object prev_pos
, front_sticky
;
1780 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1783 XSETBUFFER (buffer
, current_buffer
);
1785 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1786 /* Consider previous character. */
1788 Lisp_Object rear_non_sticky
;
1790 prev_pos
= make_number (XINT (pos
) - 1);
1791 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1793 if (!NILP (CONSP (rear_non_sticky
)
1794 ? Fmemq (prop
, rear_non_sticky
)
1796 /* PROP is rear-non-sticky. */
1802 /* Consider following character. */
1803 /* This signals an arg-out-of-range error if pos is outside the
1804 buffer's accessible range. */
1805 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1807 if (EQ (front_sticky
, Qt
)
1808 || (CONSP (front_sticky
)
1809 && !NILP (Fmemq (prop
, front_sticky
))))
1810 /* PROP is inherited from after. */
1811 is_front_sticky
= 1;
1813 /* Simple cases, where the properties are consistent. */
1814 if (is_rear_sticky
&& !is_front_sticky
)
1816 else if (!is_rear_sticky
&& is_front_sticky
)
1818 else if (!is_rear_sticky
&& !is_front_sticky
)
1821 /* The stickiness properties are inconsistent, so we have to
1822 disambiguate. Basically, rear-sticky wins, _except_ if the
1823 property that would be inherited has a value of nil, in which case
1824 front-sticky wins. */
1825 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1826 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1833 /* I don't think this is the right interface to export; how often do you
1834 want to do something like this, other than when you're copying objects
1837 I think it would be better to have a pair of functions, one which
1838 returns the text properties of a region as a list of ranges and
1839 plists, and another which applies such a list to another object. */
1841 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1842 SRC and DEST may each refer to strings or buffers.
1843 Optional sixth argument PROP causes only that property to be copied.
1844 Properties are copied to DEST as if by `add-text-properties'.
1845 Return t if any property value actually changed, nil otherwise. */
1847 /* Note this can GC when DEST is a buffer. */
1850 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1851 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1857 int s
, e
, e2
, p
, len
, modified
= 0;
1858 struct gcpro gcpro1
, gcpro2
;
1860 i
= validate_interval_range (src
, &start
, &end
, soft
);
1861 if (NULL_INTERVAL_P (i
))
1864 CHECK_NUMBER_COERCE_MARKER (pos
);
1866 Lisp_Object dest_start
, dest_end
;
1869 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1870 /* Apply this to a copy of pos; it will try to increment its arguments,
1871 which we don't want. */
1872 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1883 e2
= i
->position
+ LENGTH (i
);
1890 while (! NILP (plist
))
1892 if (EQ (Fcar (plist
), prop
))
1894 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1897 plist
= Fcdr (Fcdr (plist
));
1901 /* Must defer modifications to the interval tree in case src
1902 and dest refer to the same string or buffer. */
1903 stuff
= Fcons (Fcons (make_number (p
),
1904 Fcons (make_number (p
+ len
),
1905 Fcons (plist
, Qnil
))),
1909 i
= next_interval (i
);
1910 if (NULL_INTERVAL_P (i
))
1917 GCPRO2 (stuff
, dest
);
1919 while (! NILP (stuff
))
1922 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1923 Fcar (Fcdr (Fcdr (res
))), dest
);
1926 stuff
= Fcdr (stuff
);
1931 return modified
? Qt
: Qnil
;
1935 /* Return a list representing the text properties of OBJECT between
1936 START and END. if PROP is non-nil, report only on that property.
1937 Each result list element has the form (S E PLIST), where S and E
1938 are positions in OBJECT and PLIST is a property list containing the
1939 text properties of OBJECT between S and E. Value is nil if OBJECT
1940 doesn't contain text properties between START and END. */
1943 text_property_list (object
, start
, end
, prop
)
1944 Lisp_Object object
, start
, end
, prop
;
1951 i
= validate_interval_range (object
, &start
, &end
, soft
);
1952 if (!NULL_INTERVAL_P (i
))
1954 int s
= XINT (start
);
1959 int interval_end
, len
;
1962 interval_end
= i
->position
+ LENGTH (i
);
1963 if (interval_end
> e
)
1965 len
= interval_end
- s
;
1970 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1971 if (EQ (XCAR (plist
), prop
))
1973 plist
= Fcons (prop
, Fcons (Fcar (XCDR (plist
)), Qnil
));
1978 result
= Fcons (Fcons (make_number (s
),
1979 Fcons (make_number (s
+ len
),
1980 Fcons (plist
, Qnil
))),
1983 i
= next_interval (i
);
1984 if (NULL_INTERVAL_P (i
))
1994 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1995 (START END PLIST), where START and END are positions and PLIST is a
1996 property list containing the text properties to add. Adjust START
1997 and END positions by DELTA before adding properties. Value is
1998 non-zero if OBJECT was modified. */
2001 add_text_properties_from_list (object
, list
, delta
)
2002 Lisp_Object object
, list
, delta
;
2004 struct gcpro gcpro1
, gcpro2
;
2007 GCPRO2 (list
, object
);
2009 for (; CONSP (list
); list
= XCDR (list
))
2011 Lisp_Object item
, start
, end
, plist
, tem
;
2014 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
2015 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
2016 plist
= XCAR (XCDR (XCDR (item
)));
2018 tem
= Fadd_text_properties (start
, end
, plist
, object
);
2029 /* Modify end-points of ranges in LIST destructively. LIST is a list
2030 as returned from text_property_list. Change end-points equal to
2031 OLD_END to NEW_END. */
2034 extend_property_ranges (list
, old_end
, new_end
)
2035 Lisp_Object list
, old_end
, new_end
;
2037 for (; CONSP (list
); list
= XCDR (list
))
2039 Lisp_Object item
, end
;
2042 end
= XCAR (XCDR (item
));
2044 if (EQ (end
, old_end
))
2045 XSETCAR (XCDR (item
), new_end
);
2051 /* Call the modification hook functions in LIST, each with START and END. */
2054 call_mod_hooks (list
, start
, end
)
2055 Lisp_Object list
, start
, end
;
2057 struct gcpro gcpro1
;
2059 while (!NILP (list
))
2061 call2 (Fcar (list
), start
, end
);
2067 /* Check for read-only intervals between character positions START ... END,
2068 in BUF, and signal an error if we find one.
2070 Then check for any modification hooks in the range.
2071 Create a list of all these hooks in lexicographic order,
2072 eliminating consecutive extra copies of the same hook. Then call
2073 those hooks in order, with START and END - 1 as arguments. */
2076 verify_interval_modification (buf
, start
, end
)
2080 register INTERVAL intervals
= BUF_INTERVALS (buf
);
2081 register INTERVAL i
;
2083 register Lisp_Object prev_mod_hooks
;
2084 Lisp_Object mod_hooks
;
2085 struct gcpro gcpro1
;
2088 prev_mod_hooks
= Qnil
;
2091 interval_insert_behind_hooks
= Qnil
;
2092 interval_insert_in_front_hooks
= Qnil
;
2094 if (NULL_INTERVAL_P (intervals
))
2104 /* For an insert operation, check the two chars around the position. */
2107 INTERVAL prev
= NULL
;
2108 Lisp_Object before
, after
;
2110 /* Set I to the interval containing the char after START,
2111 and PREV to the interval containing the char before START.
2112 Either one may be null. They may be equal. */
2113 i
= find_interval (intervals
, start
);
2115 if (start
== BUF_BEGV (buf
))
2117 else if (i
->position
== start
)
2118 prev
= previous_interval (i
);
2119 else if (i
->position
< start
)
2121 if (start
== BUF_ZV (buf
))
2124 /* If Vinhibit_read_only is set and is not a list, we can
2125 skip the read_only checks. */
2126 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2128 /* If I and PREV differ we need to check for the read-only
2129 property together with its stickiness. If either I or
2130 PREV are 0, this check is all we need.
2131 We have to take special care, since read-only may be
2132 indirectly defined via the category property. */
2135 if (! NULL_INTERVAL_P (i
))
2137 after
= textget (i
->plist
, Qread_only
);
2139 /* If interval I is read-only and read-only is
2140 front-sticky, inhibit insertion.
2141 Check for read-only as well as category. */
2143 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2147 tem
= textget (i
->plist
, Qfront_sticky
);
2148 if (TMEM (Qread_only
, tem
)
2149 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2150 && TMEM (Qcategory
, tem
)))
2151 text_read_only (after
);
2155 if (! NULL_INTERVAL_P (prev
))
2157 before
= textget (prev
->plist
, Qread_only
);
2159 /* If interval PREV is read-only and read-only isn't
2160 rear-nonsticky, inhibit insertion.
2161 Check for read-only as well as category. */
2163 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2167 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2168 if (! TMEM (Qread_only
, tem
)
2169 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2170 || ! TMEM (Qcategory
, tem
)))
2171 text_read_only (before
);
2175 else if (! NULL_INTERVAL_P (i
))
2177 after
= textget (i
->plist
, Qread_only
);
2179 /* If interval I is read-only and read-only is
2180 front-sticky, inhibit insertion.
2181 Check for read-only as well as category. */
2182 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2186 tem
= textget (i
->plist
, Qfront_sticky
);
2187 if (TMEM (Qread_only
, tem
)
2188 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2189 && TMEM (Qcategory
, tem
)))
2190 text_read_only (after
);
2192 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2193 if (! TMEM (Qread_only
, tem
)
2194 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2195 || ! TMEM (Qcategory
, tem
)))
2196 text_read_only (after
);
2201 /* Run both insert hooks (just once if they're the same). */
2202 if (!NULL_INTERVAL_P (prev
))
2203 interval_insert_behind_hooks
2204 = textget (prev
->plist
, Qinsert_behind_hooks
);
2205 if (!NULL_INTERVAL_P (i
))
2206 interval_insert_in_front_hooks
2207 = textget (i
->plist
, Qinsert_in_front_hooks
);
2211 /* Loop over intervals on or next to START...END,
2212 collecting their hooks. */
2214 i
= find_interval (intervals
, start
);
2217 if (! INTERVAL_WRITABLE_P (i
))
2218 text_read_only (textget (i
->plist
, Qread_only
));
2220 if (!inhibit_modification_hooks
)
2222 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2223 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2225 hooks
= Fcons (mod_hooks
, hooks
);
2226 prev_mod_hooks
= mod_hooks
;
2230 i
= next_interval (i
);
2232 /* Keep going thru the interval containing the char before END. */
2233 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2235 if (!inhibit_modification_hooks
)
2238 hooks
= Fnreverse (hooks
);
2239 while (! EQ (hooks
, Qnil
))
2241 call_mod_hooks (Fcar (hooks
), make_number (start
),
2243 hooks
= Fcdr (hooks
);
2250 /* Run the interval hooks for an insertion on character range START ... END.
2251 verify_interval_modification chose which hooks to run;
2252 this function is called after the insertion happens
2253 so it can indicate the range of inserted text. */
2256 report_interval_modification (start
, end
)
2257 Lisp_Object start
, end
;
2259 if (! NILP (interval_insert_behind_hooks
))
2260 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2261 if (! NILP (interval_insert_in_front_hooks
)
2262 && ! EQ (interval_insert_in_front_hooks
,
2263 interval_insert_behind_hooks
))
2264 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2270 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
2271 doc
: /* Property-list used as default values.
2272 The value of a property in this list is seen as the value for every
2273 character that does not have its own value for that property. */);
2274 Vdefault_text_properties
= Qnil
;
2276 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist
,
2277 doc
: /* Alist of alternative properties for properties without a value.
2278 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2279 If a piece of text has no direct value for a particular property, then
2280 this alist is consulted. If that property appears in the alist, then
2281 the first non-nil value from the associated alternative properties is
2283 Vchar_property_alias_alist
= Qnil
;
2285 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
2286 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2287 This also inhibits the use of the `intangible' text property. */);
2288 Vinhibit_point_motion_hooks
= Qnil
;
2290 DEFVAR_LISP ("text-property-default-nonsticky",
2291 &Vtext_property_default_nonsticky
,
2292 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2293 Each element has the form (PROPERTY . NONSTICKINESS).
2295 If a character in a buffer has PROPERTY, new text inserted adjacent to
2296 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2297 inherits it if NONSTICKINESS is nil. The front-sticky and
2298 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2299 /* Text property `syntax-table' should be nonsticky by default. */
2300 Vtext_property_default_nonsticky
2301 = Fcons (Fcons (intern ("syntax-table"), Qt
), Qnil
);
2303 staticpro (&interval_insert_behind_hooks
);
2304 staticpro (&interval_insert_in_front_hooks
);
2305 interval_insert_behind_hooks
= Qnil
;
2306 interval_insert_in_front_hooks
= Qnil
;
2309 /* Common attributes one might give text */
2311 staticpro (&Qforeground
);
2312 Qforeground
= intern ("foreground");
2313 staticpro (&Qbackground
);
2314 Qbackground
= intern ("background");
2316 Qfont
= intern ("font");
2317 staticpro (&Qstipple
);
2318 Qstipple
= intern ("stipple");
2319 staticpro (&Qunderline
);
2320 Qunderline
= intern ("underline");
2321 staticpro (&Qread_only
);
2322 Qread_only
= intern ("read-only");
2323 staticpro (&Qinvisible
);
2324 Qinvisible
= intern ("invisible");
2325 staticpro (&Qintangible
);
2326 Qintangible
= intern ("intangible");
2327 staticpro (&Qcategory
);
2328 Qcategory
= intern ("category");
2329 staticpro (&Qlocal_map
);
2330 Qlocal_map
= intern ("local-map");
2331 staticpro (&Qfront_sticky
);
2332 Qfront_sticky
= intern ("front-sticky");
2333 staticpro (&Qrear_nonsticky
);
2334 Qrear_nonsticky
= intern ("rear-nonsticky");
2335 staticpro (&Qmouse_face
);
2336 Qmouse_face
= intern ("mouse-face");
2338 /* Properties that text might use to specify certain actions */
2340 staticpro (&Qmouse_left
);
2341 Qmouse_left
= intern ("mouse-left");
2342 staticpro (&Qmouse_entered
);
2343 Qmouse_entered
= intern ("mouse-entered");
2344 staticpro (&Qpoint_left
);
2345 Qpoint_left
= intern ("point-left");
2346 staticpro (&Qpoint_entered
);
2347 Qpoint_entered
= intern ("point-entered");
2349 defsubr (&Stext_properties_at
);
2350 defsubr (&Sget_text_property
);
2351 defsubr (&Sget_char_property
);
2352 defsubr (&Sget_char_property_and_overlay
);
2353 defsubr (&Snext_char_property_change
);
2354 defsubr (&Sprevious_char_property_change
);
2355 defsubr (&Snext_single_char_property_change
);
2356 defsubr (&Sprevious_single_char_property_change
);
2357 defsubr (&Snext_property_change
);
2358 defsubr (&Snext_single_property_change
);
2359 defsubr (&Sprevious_property_change
);
2360 defsubr (&Sprevious_single_property_change
);
2361 defsubr (&Sadd_text_properties
);
2362 defsubr (&Sput_text_property
);
2363 defsubr (&Sset_text_properties
);
2364 defsubr (&Sremove_text_properties
);
2365 defsubr (&Sremove_list_of_text_properties
);
2366 defsubr (&Stext_property_any
);
2367 defsubr (&Stext_property_not_all
);
2368 /* defsubr (&Serase_text_properties); */
2369 /* defsubr (&Scopy_text_properties); */
2372 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2373 (do not change this comment) */