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, 2009 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
;
60 Lisp_Object Qminibuffer_prompt
;
62 /* Sticky properties */
63 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
65 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
66 the o1's cdr. Otherwise, return zero. This is handy for
68 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70 Lisp_Object Vinhibit_point_motion_hooks
;
71 Lisp_Object Vdefault_text_properties
;
72 Lisp_Object Vchar_property_alias_alist
;
73 Lisp_Object Vtext_property_default_nonsticky
;
75 /* verify_interval_modification saves insertion hooks here
76 to be run later by report_interval_modification. */
77 Lisp_Object interval_insert_behind_hooks
;
78 Lisp_Object interval_insert_in_front_hooks
;
80 static void text_read_only
P_ ((Lisp_Object
)) NO_RETURN
;
83 /* Signal a `text-read-only' error. This function makes it easier
84 to capture that error in GDB by putting a breakpoint on it. */
87 text_read_only (propval
)
90 if (STRINGP (propval
))
91 xsignal1 (Qtext_read_only
, propval
);
93 xsignal0 (Qtext_read_only
);
98 /* Extract the interval at the position pointed to by BEGIN from
99 OBJECT, a string or buffer. Additionally, check that the positions
100 pointed to by BEGIN and END are within the bounds of OBJECT, and
101 reverse them if *BEGIN is greater than *END. The objects pointed
102 to by BEGIN and END may be integers or markers; if the latter, they
103 are coerced to integers.
105 When OBJECT is a string, we increment *BEGIN and *END
106 to make them origin-one.
108 Note that buffer points don't correspond to interval indices.
109 For example, point-max is 1 greater than the index of the last
110 character. This difference is handled in the caller, which uses
111 the validated points to determine a length, and operates on that.
112 Exceptions are Ftext_properties_at, Fnext_property_change, and
113 Fprevious_property_change which call this function with BEGIN == END.
114 Handle this case specially.
116 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
117 create an interval tree for OBJECT if one doesn't exist, provided
118 the object actually contains text. In the current design, if there
119 is no text, there can be no text properties. */
125 validate_interval_range (object
, begin
, end
, force
)
126 Lisp_Object object
, *begin
, *end
;
132 CHECK_STRING_OR_BUFFER (object
);
133 CHECK_NUMBER_COERCE_MARKER (*begin
);
134 CHECK_NUMBER_COERCE_MARKER (*end
);
136 /* If we are asked for a point, but from a subr which operates
137 on a range, then return nothing. */
138 if (EQ (*begin
, *end
) && begin
!= end
)
139 return NULL_INTERVAL
;
141 if (XINT (*begin
) > XINT (*end
))
149 if (BUFFERP (object
))
151 register struct buffer
*b
= XBUFFER (object
);
153 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
154 && XINT (*end
) <= BUF_ZV (b
)))
155 args_out_of_range (*begin
, *end
);
156 i
= BUF_INTERVALS (b
);
158 /* If there's no text, there are no properties. */
159 if (BUF_BEGV (b
) == BUF_ZV (b
))
160 return NULL_INTERVAL
;
162 searchpos
= XINT (*begin
);
166 int len
= SCHARS (object
);
168 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
169 && XINT (*end
) <= len
))
170 args_out_of_range (*begin
, *end
);
171 XSETFASTINT (*begin
, XFASTINT (*begin
));
173 XSETFASTINT (*end
, XFASTINT (*end
));
174 i
= STRING_INTERVALS (object
);
177 return NULL_INTERVAL
;
179 searchpos
= XINT (*begin
);
182 if (NULL_INTERVAL_P (i
))
183 return (force
? create_root_interval (object
) : i
);
185 return find_interval (i
, searchpos
);
188 /* Validate LIST as a property list. If LIST is not a list, then
189 make one consisting of (LIST nil). Otherwise, verify that LIST
190 is even numbered and thus suitable as a plist. */
193 validate_plist (list
)
202 register Lisp_Object tail
;
203 for (i
= 0, tail
= list
; CONSP (tail
); i
++)
209 error ("Odd length text property list");
213 return Fcons (list
, Fcons (Qnil
, Qnil
));
216 /* Return nonzero if interval I has all the properties,
217 with the same values, of list PLIST. */
220 interval_has_all_properties (plist
, i
)
224 register Lisp_Object tail1
, tail2
, sym1
;
227 /* Go through each element of PLIST. */
228 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
233 /* Go through I's plist, looking for sym1 */
234 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
235 if (EQ (sym1
, XCAR (tail2
)))
237 /* Found the same property on both lists. If the
238 values are unequal, return zero. */
239 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
242 /* Property has same value on both lists; go to next one. */
254 /* Return nonzero if the plist of interval I has any of the
255 properties of PLIST, regardless of their values. */
258 interval_has_some_properties (plist
, i
)
262 register Lisp_Object tail1
, tail2
, sym
;
264 /* Go through each element of PLIST. */
265 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
269 /* Go through i's plist, looking for tail1 */
270 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
271 if (EQ (sym
, XCAR (tail2
)))
278 /* Return nonzero if the plist of interval I has any of the
279 property names in LIST, regardless of their values. */
282 interval_has_some_properties_list (list
, i
)
286 register Lisp_Object tail1
, tail2
, sym
;
288 /* Go through each element of LIST. */
289 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
293 /* Go through i's plist, looking for tail1 */
294 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
295 if (EQ (sym
, XCAR (tail2
)))
302 /* Changing the plists of individual intervals. */
304 /* Return the value of PROP in property-list PLIST, or Qunbound if it
307 property_value (plist
, prop
)
308 Lisp_Object plist
, prop
;
312 while (PLIST_ELT_P (plist
, value
))
313 if (EQ (XCAR (plist
), prop
))
316 plist
= XCDR (value
);
321 /* Set the properties of INTERVAL to PROPERTIES,
322 and record undo info for the previous values.
323 OBJECT is the string or buffer that INTERVAL belongs to. */
326 set_properties (properties
, interval
, object
)
327 Lisp_Object properties
, object
;
330 Lisp_Object sym
, value
;
332 if (BUFFERP (object
))
334 /* For each property in the old plist which is missing from PROPERTIES,
335 or has a different value in PROPERTIES, make an undo record. */
336 for (sym
= interval
->plist
;
337 PLIST_ELT_P (sym
, value
);
339 if (! EQ (property_value (properties
, XCAR (sym
)),
342 record_property_change (interval
->position
, LENGTH (interval
),
343 XCAR (sym
), XCAR (value
),
347 /* For each new property that has no value at all in the old plist,
348 make an undo record binding it to nil, so it will be removed. */
349 for (sym
= properties
;
350 PLIST_ELT_P (sym
, value
);
352 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
354 record_property_change (interval
->position
, LENGTH (interval
),
360 /* Store new properties. */
361 interval
->plist
= Fcopy_sequence (properties
);
364 /* Add the properties of PLIST to the interval I, or set
365 the value of I's property to the value of the property on PLIST
366 if they are different.
368 OBJECT should be the string or buffer the interval is in.
370 Return nonzero if this changes I (i.e., if any members of PLIST
371 are actually added to I's plist) */
374 add_properties (plist
, i
, object
)
379 Lisp_Object tail1
, tail2
, sym1
, val1
;
380 register int changed
= 0;
382 struct gcpro gcpro1
, gcpro2
, gcpro3
;
387 /* No need to protect OBJECT, because we can GC only in the case
388 where it is a buffer, and live buffers are always protected.
389 I and its plist are also protected, via OBJECT. */
390 GCPRO3 (tail1
, sym1
, val1
);
392 /* Go through each element of PLIST. */
393 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
396 val1
= Fcar (XCDR (tail1
));
399 /* Go through I's plist, looking for sym1 */
400 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
401 if (EQ (sym1
, XCAR (tail2
)))
403 /* No need to gcpro, because tail2 protects this
404 and it must be a cons cell (we get an error otherwise). */
405 register Lisp_Object this_cdr
;
407 this_cdr
= XCDR (tail2
);
408 /* Found the property. Now check its value. */
411 /* The properties have the same value on both lists.
412 Continue to the next property. */
413 if (EQ (val1
, Fcar (this_cdr
)))
416 /* Record this change in the buffer, for undo purposes. */
417 if (BUFFERP (object
))
419 record_property_change (i
->position
, LENGTH (i
),
420 sym1
, Fcar (this_cdr
), object
);
423 /* I's property has a different value -- change it */
424 Fsetcar (this_cdr
, val1
);
431 /* Record this change in the buffer, for undo purposes. */
432 if (BUFFERP (object
))
434 record_property_change (i
->position
, LENGTH (i
),
437 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
447 /* For any members of PLIST, or LIST,
448 which are properties of I, remove them from I's plist.
449 (If PLIST is non-nil, use that, otherwise use LIST.)
450 OBJECT is the string or buffer containing I. */
453 remove_properties (plist
, list
, i
, object
)
454 Lisp_Object plist
, list
;
458 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
459 register int changed
= 0;
461 /* Nonzero means tail1 is a plist, otherwise it is a list. */
464 current_plist
= i
->plist
;
467 tail1
= plist
, use_plist
= 1;
469 tail1
= list
, use_plist
= 0;
471 /* Go through each element of LIST or PLIST. */
472 while (CONSP (tail1
))
476 /* First, remove the symbol if it's at the head of the list */
477 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
479 if (BUFFERP (object
))
480 record_property_change (i
->position
, LENGTH (i
),
481 sym
, XCAR (XCDR (current_plist
)),
484 current_plist
= XCDR (XCDR (current_plist
));
488 /* Go through I's plist, looking for SYM. */
489 tail2
= current_plist
;
490 while (! NILP (tail2
))
492 register Lisp_Object
this;
493 this = XCDR (XCDR (tail2
));
494 if (CONSP (this) && EQ (sym
, XCAR (this)))
496 if (BUFFERP (object
))
497 record_property_change (i
->position
, LENGTH (i
),
498 sym
, XCAR (XCDR (this)), object
);
500 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
506 /* Advance thru TAIL1 one way or the other. */
507 tail1
= XCDR (tail1
);
508 if (use_plist
&& CONSP (tail1
))
509 tail1
= XCDR (tail1
);
513 i
->plist
= current_plist
;
518 /* Remove all properties from interval I. Return non-zero
519 if this changes the interval. */
533 /* Returns the interval of POSITION in OBJECT.
534 POSITION is BEG-based. */
537 interval_of (position
, object
)
545 XSETBUFFER (object
, current_buffer
);
546 else if (EQ (object
, Qt
))
547 return NULL_INTERVAL
;
549 CHECK_STRING_OR_BUFFER (object
);
551 if (BUFFERP (object
))
553 register struct buffer
*b
= XBUFFER (object
);
557 i
= BUF_INTERVALS (b
);
562 end
= SCHARS (object
);
563 i
= STRING_INTERVALS (object
);
566 if (!(beg
<= position
&& position
<= end
))
567 args_out_of_range (make_number (position
), make_number (position
));
568 if (beg
== end
|| NULL_INTERVAL_P (i
))
569 return NULL_INTERVAL
;
571 return find_interval (i
, position
);
574 DEFUN ("text-properties-at", Ftext_properties_at
,
575 Stext_properties_at
, 1, 2, 0,
576 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
577 If the optional second argument OBJECT is a buffer (or nil, which means
578 the current buffer), POSITION is a buffer position (integer or marker).
579 If OBJECT is a string, POSITION is a 0-based index into it.
580 If POSITION is at the end of OBJECT, the value is nil. */)
582 Lisp_Object position
, object
;
587 XSETBUFFER (object
, current_buffer
);
589 i
= validate_interval_range (object
, &position
, &position
, soft
);
590 if (NULL_INTERVAL_P (i
))
592 /* If POSITION is at the end of the interval,
593 it means it's the end of OBJECT.
594 There are no properties at the very end,
595 since no character follows. */
596 if (XINT (position
) == LENGTH (i
) + i
->position
)
602 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
603 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
604 OBJECT is optional and defaults to the current buffer.
605 If POSITION is at the end of OBJECT, the value is nil. */)
606 (position
, prop
, object
)
607 Lisp_Object position
, object
;
610 return textget (Ftext_properties_at (position
, object
), prop
);
613 /* Return the value of char's property PROP, in OBJECT at POSITION.
614 OBJECT is optional and defaults to the current buffer.
615 If OVERLAY is non-0, then in the case that the returned property is from
616 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
617 returned in *OVERLAY.
618 If POSITION is at the end of OBJECT, the value is nil.
619 If OBJECT is a buffer, then overlay properties are considered as well as
621 If OBJECT is a window, then that window's buffer is used, but
622 window-specific overlays are considered only if they are associated
625 get_char_property_and_overlay (position
, prop
, object
, overlay
)
626 Lisp_Object position
, object
;
627 register Lisp_Object prop
;
628 Lisp_Object
*overlay
;
630 struct window
*w
= 0;
632 CHECK_NUMBER_COERCE_MARKER (position
);
635 XSETBUFFER (object
, current_buffer
);
637 if (WINDOWP (object
))
639 w
= XWINDOW (object
);
642 if (BUFFERP (object
))
645 Lisp_Object
*overlay_vec
;
646 struct buffer
*obuf
= current_buffer
;
648 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
649 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
650 xsignal1 (Qargs_out_of_range
, position
);
652 set_buffer_temp (XBUFFER (object
));
654 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
655 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
657 set_buffer_temp (obuf
);
659 /* Now check the overlays in order of decreasing priority. */
660 while (--noverlays
>= 0)
662 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
666 /* Return the overlay we got the property from. */
667 *overlay
= overlay_vec
[noverlays
];
674 /* Indicate that the return value is not from an overlay. */
677 /* Not a buffer, or no appropriate overlay, so fall through to the
679 return Fget_text_property (position
, prop
, object
);
682 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
683 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
684 Both overlay properties and text properties are checked.
685 OBJECT is optional and defaults to the current buffer.
686 If POSITION is at the end of OBJECT, the value is nil.
687 If OBJECT is a buffer, then overlay properties are considered as well as
689 If OBJECT is a window, then that window's buffer is used, but window-specific
690 overlays are considered only if they are associated with OBJECT. */)
691 (position
, prop
, object
)
692 Lisp_Object position
, object
;
693 register Lisp_Object prop
;
695 return get_char_property_and_overlay (position
, prop
, object
, 0);
698 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
699 Sget_char_property_and_overlay
, 2, 3, 0,
700 doc
: /* Like `get-char-property', but with extra overlay information.
701 The value is a cons cell. Its car is the return value of `get-char-property'
702 with the same arguments--that is, the value of POSITION's property
703 PROP in OBJECT. Its cdr is the overlay in which the property was
704 found, or nil, if it was found as a text property or not found at all.
706 OBJECT is optional and defaults to the current buffer. OBJECT may be
707 a string, a buffer or a window. For strings, the cdr of the return
708 value is always nil, since strings do not have overlays. If OBJECT is
709 a window, then that window's buffer is used, but window-specific
710 overlays are considered only if they are associated with OBJECT. If
711 POSITION is at the end of OBJECT, both car and cdr are nil. */)
712 (position
, prop
, object
)
713 Lisp_Object position
, object
;
714 register Lisp_Object prop
;
718 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
719 return Fcons (val
, overlay
);
723 DEFUN ("next-char-property-change", Fnext_char_property_change
,
724 Snext_char_property_change
, 1, 2, 0,
725 doc
: /* Return the position of next text property or overlay change.
726 This scans characters forward in the current buffer from POSITION till
727 it finds a change in some text property, or the beginning or end of an
728 overlay, and returns the position of that.
729 If none is found up to (point-max), the function returns (point-max).
731 If the optional second argument LIMIT is non-nil, don't search
732 past position LIMIT; return LIMIT if nothing is found before LIMIT.
733 LIMIT is a no-op if it is greater than (point-max). */)
735 Lisp_Object position
, limit
;
739 temp
= Fnext_overlay_change (position
);
742 CHECK_NUMBER_COERCE_MARKER (limit
);
743 if (XINT (limit
) < XINT (temp
))
746 return Fnext_property_change (position
, Qnil
, temp
);
749 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
750 Sprevious_char_property_change
, 1, 2, 0,
751 doc
: /* Return the position of previous text property or overlay change.
752 Scans characters backward in the current buffer from POSITION till it
753 finds a change in some text property, or the beginning or end of an
754 overlay, and returns the position of that.
755 If none is found since (point-min), the function returns (point-min).
757 If the optional second argument LIMIT is non-nil, don't search
758 past position LIMIT; return LIMIT if nothing is found before LIMIT.
759 LIMIT is a no-op if it is less than (point-min). */)
761 Lisp_Object position
, limit
;
765 temp
= Fprevious_overlay_change (position
);
768 CHECK_NUMBER_COERCE_MARKER (limit
);
769 if (XINT (limit
) > XINT (temp
))
772 return Fprevious_property_change (position
, Qnil
, temp
);
776 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
777 Snext_single_char_property_change
, 2, 4, 0,
778 doc
: /* Return the position of next text property or overlay change for a specific property.
779 Scans characters forward from POSITION till it finds
780 a change in the PROP property, then returns the position of the change.
781 If the optional third argument OBJECT is a buffer (or nil, which means
782 the current buffer), POSITION is a buffer position (integer or marker).
783 If OBJECT is a string, POSITION is a 0-based index into it.
785 In a string, scan runs to the end of the string.
786 In a buffer, it runs to (point-max), and the value cannot exceed that.
788 The property values are compared with `eq'.
789 If the property is constant all the way to the end of OBJECT, return the
790 last valid position in OBJECT.
791 If the optional fourth argument LIMIT is non-nil, don't search
792 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
793 (position
, prop
, object
, limit
)
794 Lisp_Object prop
, position
, object
, limit
;
796 if (STRINGP (object
))
798 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
802 position
= make_number (SCHARS (object
));
805 CHECK_NUMBER (limit
);
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 CHECK_NUMBER_COERCE_MARKER (position
);
826 initial_value
= Fget_char_property (position
, prop
, object
);
829 XSETFASTINT (limit
, ZV
);
831 CHECK_NUMBER_COERCE_MARKER (limit
);
833 if (XFASTINT (position
) >= XFASTINT (limit
))
836 if (XFASTINT (position
) > ZV
)
837 XSETFASTINT (position
, ZV
);
842 position
= Fnext_char_property_change (position
, limit
);
843 if (XFASTINT (position
) >= XFASTINT (limit
))
849 value
= Fget_char_property (position
, prop
, object
);
850 if (!EQ (value
, initial_value
))
854 unbind_to (count
, Qnil
);
860 DEFUN ("previous-single-char-property-change",
861 Fprevious_single_char_property_change
,
862 Sprevious_single_char_property_change
, 2, 4, 0,
863 doc
: /* Return the position of previous text property or overlay change for a specific property.
864 Scans characters backward from POSITION till it finds
865 a change in the PROP property, then returns the position of the change.
866 If the optional third argument OBJECT is a buffer (or nil, which means
867 the current buffer), POSITION is a buffer position (integer or marker).
868 If OBJECT is a string, POSITION is a 0-based index into it.
870 In a string, scan runs to the start of the string.
871 In a buffer, it runs to (point-min), and the value cannot be less than that.
873 The property values are compared with `eq'.
874 If the property is constant all the way to the start of OBJECT, return the
875 first valid position in OBJECT.
876 If the optional fourth argument LIMIT is non-nil, don't search
877 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
878 (position
, prop
, object
, limit
)
879 Lisp_Object prop
, position
, object
, limit
;
881 if (STRINGP (object
))
883 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
887 position
= make_number (0);
890 CHECK_NUMBER (limit
);
897 int count
= SPECPDL_INDEX ();
900 CHECK_BUFFER (object
);
902 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
904 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
905 Fset_buffer (object
);
908 CHECK_NUMBER_COERCE_MARKER (position
);
911 XSETFASTINT (limit
, BEGV
);
913 CHECK_NUMBER_COERCE_MARKER (limit
);
915 if (XFASTINT (position
) <= XFASTINT (limit
))
918 if (XFASTINT (position
) < BEGV
)
919 XSETFASTINT (position
, BEGV
);
923 Lisp_Object initial_value
924 = Fget_char_property (make_number (XFASTINT (position
) - 1),
929 position
= Fprevious_char_property_change (position
, limit
);
931 if (XFASTINT (position
) <= XFASTINT (limit
))
939 = Fget_char_property (make_number (XFASTINT (position
) - 1),
942 if (!EQ (value
, initial_value
))
948 unbind_to (count
, Qnil
);
954 DEFUN ("next-property-change", Fnext_property_change
,
955 Snext_property_change
, 1, 3, 0,
956 doc
: /* Return the position of next property change.
957 Scans characters forward from POSITION in OBJECT till it finds
958 a change in some text property, then returns the position of the change.
959 If the optional second argument OBJECT is a buffer (or nil, which means
960 the current buffer), POSITION is a buffer position (integer or marker).
961 If OBJECT is a string, POSITION is a 0-based index into it.
962 Return nil if the property is constant all the way to the end of OBJECT.
963 If the value is non-nil, it is a position greater than POSITION, never equal.
965 If the optional third argument LIMIT is non-nil, don't search
966 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
967 (position
, object
, limit
)
968 Lisp_Object position
, object
, limit
;
970 register INTERVAL i
, next
;
973 XSETBUFFER (object
, current_buffer
);
975 if (!NILP (limit
) && !EQ (limit
, Qt
))
976 CHECK_NUMBER_COERCE_MARKER (limit
);
978 i
= validate_interval_range (object
, &position
, &position
, soft
);
980 /* If LIMIT is t, return start of next interval--don't
981 bother checking further intervals. */
984 if (NULL_INTERVAL_P (i
))
987 next
= next_interval (i
);
989 if (NULL_INTERVAL_P (next
))
990 XSETFASTINT (position
, (STRINGP (object
)
992 : BUF_ZV (XBUFFER (object
))));
994 XSETFASTINT (position
, next
->position
);
998 if (NULL_INTERVAL_P (i
))
1001 next
= next_interval (i
);
1003 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
1004 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1005 next
= next_interval (next
);
1007 if (NULL_INTERVAL_P (next
)
1009 >= (INTEGERP (limit
)
1013 : BUF_ZV (XBUFFER (object
))))))
1016 return make_number (next
->position
);
1019 /* Return 1 if there's a change in some property between BEG and END. */
1022 property_change_between_p (beg
, end
)
1025 register INTERVAL i
, next
;
1026 Lisp_Object object
, pos
;
1028 XSETBUFFER (object
, current_buffer
);
1029 XSETFASTINT (pos
, beg
);
1031 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
1032 if (NULL_INTERVAL_P (i
))
1035 next
= next_interval (i
);
1036 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
1038 next
= next_interval (next
);
1039 if (NULL_INTERVAL_P (next
))
1041 if (next
->position
>= end
)
1045 if (NULL_INTERVAL_P (next
))
1051 DEFUN ("next-single-property-change", Fnext_single_property_change
,
1052 Snext_single_property_change
, 2, 4, 0,
1053 doc
: /* Return the position of next property change for a specific property.
1054 Scans characters forward from POSITION till it finds
1055 a change in the PROP property, then returns the position of the change.
1056 If the optional third argument OBJECT is a buffer (or nil, which means
1057 the current buffer), POSITION is a buffer position (integer or marker).
1058 If OBJECT is a string, POSITION is a 0-based index into it.
1059 The property values are compared with `eq'.
1060 Return nil if the property is constant all the way to the end of OBJECT.
1061 If the value is non-nil, it is a position greater than POSITION, never equal.
1063 If the optional fourth argument LIMIT is non-nil, don't search
1064 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1065 (position
, prop
, object
, limit
)
1066 Lisp_Object position
, prop
, object
, limit
;
1068 register INTERVAL i
, next
;
1069 register Lisp_Object here_val
;
1072 XSETBUFFER (object
, current_buffer
);
1075 CHECK_NUMBER_COERCE_MARKER (limit
);
1077 i
= validate_interval_range (object
, &position
, &position
, soft
);
1078 if (NULL_INTERVAL_P (i
))
1081 here_val
= textget (i
->plist
, prop
);
1082 next
= next_interval (i
);
1083 while (! NULL_INTERVAL_P (next
)
1084 && EQ (here_val
, textget (next
->plist
, prop
))
1085 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1086 next
= next_interval (next
);
1088 if (NULL_INTERVAL_P (next
)
1090 >= (INTEGERP (limit
)
1094 : BUF_ZV (XBUFFER (object
))))))
1097 return make_number (next
->position
);
1100 DEFUN ("previous-property-change", Fprevious_property_change
,
1101 Sprevious_property_change
, 1, 3, 0,
1102 doc
: /* Return the position of previous property change.
1103 Scans characters backwards from POSITION in OBJECT till it finds
1104 a change in some text property, then returns the position of the change.
1105 If the optional second argument OBJECT is a buffer (or nil, which means
1106 the current buffer), POSITION is a buffer position (integer or marker).
1107 If OBJECT is a string, POSITION is a 0-based index into it.
1108 Return nil if the property is constant all the way to the start of OBJECT.
1109 If the value is non-nil, it is a position less than POSITION, never equal.
1111 If the optional third argument LIMIT is non-nil, don't search
1112 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1113 (position
, object
, limit
)
1114 Lisp_Object position
, object
, limit
;
1116 register INTERVAL i
, previous
;
1119 XSETBUFFER (object
, current_buffer
);
1122 CHECK_NUMBER_COERCE_MARKER (limit
);
1124 i
= validate_interval_range (object
, &position
, &position
, soft
);
1125 if (NULL_INTERVAL_P (i
))
1128 /* Start with the interval containing the char before point. */
1129 if (i
->position
== XFASTINT (position
))
1130 i
= previous_interval (i
);
1132 previous
= previous_interval (i
);
1133 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1135 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1136 previous
= previous_interval (previous
);
1138 if (NULL_INTERVAL_P (previous
)
1139 || (previous
->position
+ LENGTH (previous
)
1140 <= (INTEGERP (limit
)
1142 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1145 return make_number (previous
->position
+ LENGTH (previous
));
1148 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1149 Sprevious_single_property_change
, 2, 4, 0,
1150 doc
: /* Return the position of previous property change for a specific property.
1151 Scans characters backward from POSITION till it finds
1152 a change in the PROP property, then returns the position of the change.
1153 If the optional third argument OBJECT is a buffer (or nil, which means
1154 the current buffer), POSITION is a buffer position (integer or marker).
1155 If OBJECT is a string, POSITION is a 0-based index into it.
1156 The property values are compared with `eq'.
1157 Return nil if the property is constant all the way to the start of OBJECT.
1158 If the value is non-nil, it is a position less than POSITION, never equal.
1160 If the optional fourth argument LIMIT is non-nil, don't search
1161 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1162 (position
, prop
, object
, limit
)
1163 Lisp_Object position
, prop
, object
, limit
;
1165 register INTERVAL i
, previous
;
1166 register Lisp_Object here_val
;
1169 XSETBUFFER (object
, current_buffer
);
1172 CHECK_NUMBER_COERCE_MARKER (limit
);
1174 i
= validate_interval_range (object
, &position
, &position
, soft
);
1176 /* Start with the interval containing the char before point. */
1177 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1178 i
= previous_interval (i
);
1180 if (NULL_INTERVAL_P (i
))
1183 here_val
= textget (i
->plist
, prop
);
1184 previous
= previous_interval (i
);
1185 while (!NULL_INTERVAL_P (previous
)
1186 && EQ (here_val
, textget (previous
->plist
, prop
))
1188 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1189 previous
= previous_interval (previous
);
1191 if (NULL_INTERVAL_P (previous
)
1192 || (previous
->position
+ LENGTH (previous
)
1193 <= (INTEGERP (limit
)
1195 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1198 return make_number (previous
->position
+ LENGTH (previous
));
1201 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1203 DEFUN ("add-text-properties", Fadd_text_properties
,
1204 Sadd_text_properties
, 3, 4, 0,
1205 doc
: /* Add properties to the text from START to END.
1206 The third argument PROPERTIES is a property list
1207 specifying the property values to add. If the optional fourth argument
1208 OBJECT is a buffer (or nil, which means the current buffer),
1209 START and END are buffer positions (integers or markers).
1210 If OBJECT is a string, START and END are 0-based indices into it.
1211 Return t if any property value actually changed, nil otherwise. */)
1212 (start
, end
, properties
, object
)
1213 Lisp_Object start
, end
, properties
, object
;
1215 register INTERVAL i
, unchanged
;
1216 register int s
, len
, modified
= 0;
1217 struct gcpro gcpro1
;
1219 properties
= validate_plist (properties
);
1220 if (NILP (properties
))
1224 XSETBUFFER (object
, current_buffer
);
1226 i
= validate_interval_range (object
, &start
, &end
, hard
);
1227 if (NULL_INTERVAL_P (i
))
1231 len
= XINT (end
) - s
;
1233 /* No need to protect OBJECT, because we GC only if it's a buffer,
1234 and live buffers are always protected. */
1235 GCPRO1 (properties
);
1237 /* If we're not starting on an interval boundary, we have to
1238 split this interval. */
1239 if (i
->position
!= s
)
1241 /* If this interval already has the properties, we can
1243 if (interval_has_all_properties (properties
, i
))
1245 int got
= (LENGTH (i
) - (s
- i
->position
));
1247 RETURN_UNGCPRO (Qnil
);
1249 i
= next_interval (i
);
1254 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1255 copy_properties (unchanged
, i
);
1259 if (BUFFERP (object
))
1260 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1262 /* We are at the beginning of interval I, with LEN chars to scan. */
1268 if (LENGTH (i
) >= len
)
1270 /* We can UNGCPRO safely here, because there will be just
1271 one more chance to gc, in the next call to add_properties,
1272 and after that we will not need PROPERTIES or OBJECT again. */
1275 if (interval_has_all_properties (properties
, i
))
1277 if (BUFFERP (object
))
1278 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1279 XINT (end
) - XINT (start
));
1281 return modified
? Qt
: Qnil
;
1284 if (LENGTH (i
) == len
)
1286 add_properties (properties
, i
, object
);
1287 if (BUFFERP (object
))
1288 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1289 XINT (end
) - XINT (start
));
1293 /* i doesn't have the properties, and goes past the change limit */
1295 i
= split_interval_left (unchanged
, len
);
1296 copy_properties (unchanged
, i
);
1297 add_properties (properties
, i
, object
);
1298 if (BUFFERP (object
))
1299 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1300 XINT (end
) - XINT (start
));
1305 modified
+= add_properties (properties
, i
, object
);
1306 i
= next_interval (i
);
1310 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1312 DEFUN ("put-text-property", Fput_text_property
,
1313 Sput_text_property
, 4, 5, 0,
1314 doc
: /* Set one property of the text from START to END.
1315 The third and fourth arguments PROPERTY and VALUE
1316 specify the property to add.
1317 If the optional fifth argument OBJECT is a buffer (or nil, which means
1318 the current buffer), START and END are buffer positions (integers or
1319 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1320 (start
, end
, property
, value
, object
)
1321 Lisp_Object start
, end
, property
, value
, object
;
1323 Fadd_text_properties (start
, end
,
1324 Fcons (property
, Fcons (value
, Qnil
)),
1329 DEFUN ("set-text-properties", Fset_text_properties
,
1330 Sset_text_properties
, 3, 4, 0,
1331 doc
: /* Completely replace properties of text from START to END.
1332 The third argument PROPERTIES is the new property list.
1333 If the optional fourth argument OBJECT is a buffer (or nil, which means
1334 the current buffer), START and END are buffer positions (integers or
1335 markers). If OBJECT is a string, START and END are 0-based indices into it.
1336 If PROPERTIES is nil, the effect is to remove all properties from
1337 the designated part of OBJECT. */)
1338 (start
, end
, properties
, object
)
1339 Lisp_Object start
, end
, properties
, object
;
1341 return set_text_properties (start
, end
, properties
, object
, Qt
);
1345 /* Replace properties of text from START to END with new list of
1346 properties PROPERTIES. OBJECT is the buffer or string containing
1347 the text. OBJECT nil means use the current buffer.
1348 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1349 is nil if the function _detected_ that it did not replace any
1350 properties, non-nil otherwise. */
1353 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1354 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1356 register INTERVAL i
;
1357 Lisp_Object ostart
, oend
;
1362 properties
= validate_plist (properties
);
1365 XSETBUFFER (object
, current_buffer
);
1367 /* If we want no properties for a whole string,
1368 get rid of its intervals. */
1369 if (NILP (properties
) && STRINGP (object
)
1370 && XFASTINT (start
) == 0
1371 && XFASTINT (end
) == SCHARS (object
))
1373 if (! STRING_INTERVALS (object
))
1376 STRING_SET_INTERVALS (object
, NULL_INTERVAL
);
1380 i
= validate_interval_range (object
, &start
, &end
, soft
);
1382 if (NULL_INTERVAL_P (i
))
1384 /* If buffer has no properties, and we want none, return now. */
1385 if (NILP (properties
))
1388 /* Restore the original START and END values
1389 because validate_interval_range increments them for strings. */
1393 i
= validate_interval_range (object
, &start
, &end
, hard
);
1394 /* This can return if start == end. */
1395 if (NULL_INTERVAL_P (i
))
1399 if (BUFFERP (object
))
1400 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1402 set_text_properties_1 (start
, end
, properties
, object
, i
);
1404 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1405 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1406 XINT (end
) - XINT (start
));
1410 /* Replace properties of text from START to END with new list of
1411 properties PROPERTIES. BUFFER is the buffer containing
1412 the text. This does not obey any hooks.
1413 You can provide the interval that START is located in as I,
1414 or pass NULL for I and this function will find it.
1415 START and END can be in any order. */
1418 set_text_properties_1 (start
, end
, properties
, buffer
, i
)
1419 Lisp_Object start
, end
, properties
, buffer
;
1422 register INTERVAL prev_changed
= NULL_INTERVAL
;
1423 register int s
, len
;
1427 len
= XINT (end
) - s
;
1437 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1439 if (i
->position
!= s
)
1442 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1444 if (LENGTH (i
) > len
)
1446 copy_properties (unchanged
, i
);
1447 i
= split_interval_left (i
, len
);
1448 set_properties (properties
, i
, buffer
);
1452 set_properties (properties
, i
, buffer
);
1454 if (LENGTH (i
) == len
)
1459 i
= next_interval (i
);
1462 /* We are starting at the beginning of an interval, I */
1468 if (LENGTH (i
) >= len
)
1470 if (LENGTH (i
) > len
)
1471 i
= split_interval_left (i
, len
);
1473 /* We have to call set_properties even if we are going to
1474 merge the intervals, so as to make the undo records
1475 and cause redisplay to happen. */
1476 set_properties (properties
, i
, buffer
);
1477 if (!NULL_INTERVAL_P (prev_changed
))
1478 merge_interval_left (i
);
1484 /* We have to call set_properties even if we are going to
1485 merge the intervals, so as to make the undo records
1486 and cause redisplay to happen. */
1487 set_properties (properties
, i
, buffer
);
1488 if (NULL_INTERVAL_P (prev_changed
))
1491 prev_changed
= i
= merge_interval_left (i
);
1493 i
= next_interval (i
);
1497 DEFUN ("remove-text-properties", Fremove_text_properties
,
1498 Sremove_text_properties
, 3, 4, 0,
1499 doc
: /* Remove some properties from text from START to END.
1500 The third argument PROPERTIES is a property list
1501 whose property names specify the properties to remove.
1502 \(The values stored in PROPERTIES are ignored.)
1503 If the optional fourth argument OBJECT is a buffer (or nil, which means
1504 the current buffer), START and END are buffer positions (integers or
1505 markers). If OBJECT is a string, START and END are 0-based indices into it.
1506 Return t if any property was actually removed, nil otherwise.
1508 Use `set-text-properties' if you want to remove all text properties. */)
1509 (start
, end
, properties
, object
)
1510 Lisp_Object start
, end
, properties
, object
;
1512 register INTERVAL i
, unchanged
;
1513 register int s
, len
, modified
= 0;
1516 XSETBUFFER (object
, current_buffer
);
1518 i
= validate_interval_range (object
, &start
, &end
, soft
);
1519 if (NULL_INTERVAL_P (i
))
1523 len
= XINT (end
) - s
;
1525 if (i
->position
!= s
)
1527 /* No properties on this first interval -- return if
1528 it covers the entire region. */
1529 if (! interval_has_some_properties (properties
, i
))
1531 int got
= (LENGTH (i
) - (s
- i
->position
));
1535 i
= next_interval (i
);
1537 /* Split away the beginning of this interval; what we don't
1542 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1543 copy_properties (unchanged
, i
);
1547 if (BUFFERP (object
))
1548 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1550 /* We are at the beginning of an interval, with len to scan */
1556 if (LENGTH (i
) >= len
)
1558 if (! interval_has_some_properties (properties
, i
))
1559 return modified
? Qt
: Qnil
;
1561 if (LENGTH (i
) == len
)
1563 remove_properties (properties
, Qnil
, i
, object
);
1564 if (BUFFERP (object
))
1565 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1566 XINT (end
) - XINT (start
));
1570 /* i has the properties, and goes past the change limit */
1572 i
= split_interval_left (i
, len
);
1573 copy_properties (unchanged
, i
);
1574 remove_properties (properties
, Qnil
, i
, object
);
1575 if (BUFFERP (object
))
1576 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1577 XINT (end
) - XINT (start
));
1582 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1583 i
= next_interval (i
);
1587 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1588 Sremove_list_of_text_properties
, 3, 4, 0,
1589 doc
: /* Remove some properties from text from START to END.
1590 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1591 If the optional fourth argument OBJECT is a buffer (or nil, which means
1592 the current buffer), START and END are buffer positions (integers or
1593 markers). If OBJECT is a string, START and END are 0-based indices into it.
1594 Return t if any property was actually removed, nil otherwise. */)
1595 (start
, end
, list_of_properties
, object
)
1596 Lisp_Object start
, end
, list_of_properties
, object
;
1598 register INTERVAL i
, unchanged
;
1599 register int s
, len
, modified
= 0;
1600 Lisp_Object properties
;
1601 properties
= list_of_properties
;
1604 XSETBUFFER (object
, current_buffer
);
1606 i
= validate_interval_range (object
, &start
, &end
, soft
);
1607 if (NULL_INTERVAL_P (i
))
1611 len
= XINT (end
) - s
;
1613 if (i
->position
!= s
)
1615 /* No properties on this first interval -- return if
1616 it covers the entire region. */
1617 if (! interval_has_some_properties_list (properties
, i
))
1619 int got
= (LENGTH (i
) - (s
- i
->position
));
1623 i
= next_interval (i
);
1625 /* Split away the beginning of this interval; what we don't
1630 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1631 copy_properties (unchanged
, i
);
1635 /* We are at the beginning of an interval, with len to scan.
1636 The flag `modified' records if changes have been made.
1637 When object is a buffer, we must call modify_region before changes are
1638 made and signal_after_change when we are done.
1639 We call modify_region before calling remove_properties if modified == 0,
1640 and we call signal_after_change before returning if modified != 0. */
1646 if (LENGTH (i
) >= len
)
1648 if (! interval_has_some_properties_list (properties
, i
))
1651 if (BUFFERP (object
))
1652 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1653 XINT (end
) - XINT (start
));
1659 if (LENGTH (i
) == len
)
1661 if (!modified
&& BUFFERP (object
))
1662 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1663 remove_properties (Qnil
, properties
, i
, object
);
1664 if (BUFFERP (object
))
1665 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1666 XINT (end
) - XINT (start
));
1670 /* i has the properties, and goes past the change limit */
1672 i
= split_interval_left (i
, len
);
1673 copy_properties (unchanged
, i
);
1674 if (!modified
&& BUFFERP (object
))
1675 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1676 remove_properties (Qnil
, properties
, i
, object
);
1677 if (BUFFERP (object
))
1678 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1679 XINT (end
) - XINT (start
));
1683 if (interval_has_some_properties_list (properties
, i
))
1685 if (!modified
&& BUFFERP (object
))
1686 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1687 remove_properties (Qnil
, properties
, i
, object
);
1691 i
= next_interval (i
);
1695 DEFUN ("text-property-any", Ftext_property_any
,
1696 Stext_property_any
, 4, 5, 0,
1697 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1698 If so, return the position of the first character whose property PROPERTY
1699 is `eq' to VALUE. Otherwise return nil.
1700 If the optional fifth argument OBJECT is a buffer (or nil, which means
1701 the current buffer), START and END are buffer positions (integers or
1702 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1703 (start
, end
, property
, value
, object
)
1704 Lisp_Object start
, end
, property
, value
, object
;
1706 register INTERVAL i
;
1707 register int e
, pos
;
1710 XSETBUFFER (object
, current_buffer
);
1711 i
= validate_interval_range (object
, &start
, &end
, soft
);
1712 if (NULL_INTERVAL_P (i
))
1713 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1716 while (! NULL_INTERVAL_P (i
))
1718 if (i
->position
>= e
)
1720 if (EQ (textget (i
->plist
, property
), value
))
1723 if (pos
< XINT (start
))
1725 return make_number (pos
);
1727 i
= next_interval (i
);
1732 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1733 Stext_property_not_all
, 4, 5, 0,
1734 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1735 If so, return the position of the first character whose property PROPERTY
1736 is not `eq' to VALUE. Otherwise, return nil.
1737 If the optional fifth argument OBJECT is a buffer (or nil, which means
1738 the current buffer), START and END are buffer positions (integers or
1739 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1740 (start
, end
, property
, value
, object
)
1741 Lisp_Object start
, end
, property
, value
, object
;
1743 register INTERVAL i
;
1747 XSETBUFFER (object
, current_buffer
);
1748 i
= validate_interval_range (object
, &start
, &end
, soft
);
1749 if (NULL_INTERVAL_P (i
))
1750 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1754 while (! NULL_INTERVAL_P (i
))
1756 if (i
->position
>= e
)
1758 if (! EQ (textget (i
->plist
, property
), value
))
1760 if (i
->position
> s
)
1762 return make_number (s
);
1764 i
= next_interval (i
);
1770 /* Return the direction from which the text-property PROP would be
1771 inherited by any new text inserted at POS: 1 if it would be
1772 inherited from the char after POS, -1 if it would be inherited from
1773 the char before POS, and 0 if from neither.
1774 BUFFER can be either a buffer or nil (meaning current buffer). */
1777 text_property_stickiness (prop
, pos
, buffer
)
1778 Lisp_Object prop
, pos
, buffer
;
1780 Lisp_Object prev_pos
, front_sticky
;
1781 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1784 XSETBUFFER (buffer
, current_buffer
);
1786 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1787 /* Consider previous character. */
1789 Lisp_Object rear_non_sticky
;
1791 prev_pos
= make_number (XINT (pos
) - 1);
1792 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1794 if (!NILP (CONSP (rear_non_sticky
)
1795 ? Fmemq (prop
, rear_non_sticky
)
1797 /* PROP is rear-non-sticky. */
1803 /* Consider following character. */
1804 /* This signals an arg-out-of-range error if pos is outside the
1805 buffer's accessible range. */
1806 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1808 if (EQ (front_sticky
, Qt
)
1809 || (CONSP (front_sticky
)
1810 && !NILP (Fmemq (prop
, front_sticky
))))
1811 /* PROP is inherited from after. */
1812 is_front_sticky
= 1;
1814 /* Simple cases, where the properties are consistent. */
1815 if (is_rear_sticky
&& !is_front_sticky
)
1817 else if (!is_rear_sticky
&& is_front_sticky
)
1819 else if (!is_rear_sticky
&& !is_front_sticky
)
1822 /* The stickiness properties are inconsistent, so we have to
1823 disambiguate. Basically, rear-sticky wins, _except_ if the
1824 property that would be inherited has a value of nil, in which case
1825 front-sticky wins. */
1826 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1827 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1834 /* I don't think this is the right interface to export; how often do you
1835 want to do something like this, other than when you're copying objects
1838 I think it would be better to have a pair of functions, one which
1839 returns the text properties of a region as a list of ranges and
1840 plists, and another which applies such a list to another object. */
1842 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1843 SRC and DEST may each refer to strings or buffers.
1844 Optional sixth argument PROP causes only that property to be copied.
1845 Properties are copied to DEST as if by `add-text-properties'.
1846 Return t if any property value actually changed, nil otherwise. */
1848 /* Note this can GC when DEST is a buffer. */
1851 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1852 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1858 int s
, e
, e2
, p
, len
, modified
= 0;
1859 struct gcpro gcpro1
, gcpro2
;
1861 i
= validate_interval_range (src
, &start
, &end
, soft
);
1862 if (NULL_INTERVAL_P (i
))
1865 CHECK_NUMBER_COERCE_MARKER (pos
);
1867 Lisp_Object dest_start
, dest_end
;
1870 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1871 /* Apply this to a copy of pos; it will try to increment its arguments,
1872 which we don't want. */
1873 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1884 e2
= i
->position
+ LENGTH (i
);
1891 while (! NILP (plist
))
1893 if (EQ (Fcar (plist
), prop
))
1895 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1898 plist
= Fcdr (Fcdr (plist
));
1902 /* Must defer modifications to the interval tree in case src
1903 and dest refer to the same string or buffer. */
1904 stuff
= Fcons (Fcons (make_number (p
),
1905 Fcons (make_number (p
+ len
),
1906 Fcons (plist
, Qnil
))),
1910 i
= next_interval (i
);
1911 if (NULL_INTERVAL_P (i
))
1918 GCPRO2 (stuff
, dest
);
1920 while (! NILP (stuff
))
1923 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1924 Fcar (Fcdr (Fcdr (res
))), dest
);
1927 stuff
= Fcdr (stuff
);
1932 return modified
? Qt
: Qnil
;
1936 /* Return a list representing the text properties of OBJECT between
1937 START and END. if PROP is non-nil, report only on that property.
1938 Each result list element has the form (S E PLIST), where S and E
1939 are positions in OBJECT and PLIST is a property list containing the
1940 text properties of OBJECT between S and E. Value is nil if OBJECT
1941 doesn't contain text properties between START and END. */
1944 text_property_list (object
, start
, end
, prop
)
1945 Lisp_Object object
, start
, end
, prop
;
1952 i
= validate_interval_range (object
, &start
, &end
, soft
);
1953 if (!NULL_INTERVAL_P (i
))
1955 int s
= XINT (start
);
1960 int interval_end
, len
;
1963 interval_end
= i
->position
+ LENGTH (i
);
1964 if (interval_end
> e
)
1966 len
= interval_end
- s
;
1971 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1972 if (EQ (XCAR (plist
), prop
))
1974 plist
= Fcons (prop
, Fcons (Fcar (XCDR (plist
)), Qnil
));
1979 result
= Fcons (Fcons (make_number (s
),
1980 Fcons (make_number (s
+ len
),
1981 Fcons (plist
, Qnil
))),
1984 i
= next_interval (i
);
1985 if (NULL_INTERVAL_P (i
))
1995 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1996 (START END PLIST), where START and END are positions and PLIST is a
1997 property list containing the text properties to add. Adjust START
1998 and END positions by DELTA before adding properties. Value is
1999 non-zero if OBJECT was modified. */
2002 add_text_properties_from_list (object
, list
, delta
)
2003 Lisp_Object object
, list
, delta
;
2005 struct gcpro gcpro1
, gcpro2
;
2008 GCPRO2 (list
, object
);
2010 for (; CONSP (list
); list
= XCDR (list
))
2012 Lisp_Object item
, start
, end
, plist
, tem
;
2015 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
2016 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
2017 plist
= XCAR (XCDR (XCDR (item
)));
2019 tem
= Fadd_text_properties (start
, end
, plist
, object
);
2030 /* Modify end-points of ranges in LIST destructively. LIST is a list
2031 as returned from text_property_list. Change end-points equal to
2032 OLD_END to NEW_END. */
2035 extend_property_ranges (list
, old_end
, new_end
)
2036 Lisp_Object list
, old_end
, new_end
;
2038 for (; CONSP (list
); list
= XCDR (list
))
2040 Lisp_Object item
, end
;
2043 end
= XCAR (XCDR (item
));
2045 if (EQ (end
, old_end
))
2046 XSETCAR (XCDR (item
), new_end
);
2052 /* Call the modification hook functions in LIST, each with START and END. */
2055 call_mod_hooks (list
, start
, end
)
2056 Lisp_Object list
, start
, end
;
2058 struct gcpro gcpro1
;
2060 while (!NILP (list
))
2062 call2 (Fcar (list
), start
, end
);
2068 /* Check for read-only intervals between character positions START ... END,
2069 in BUF, and signal an error if we find one.
2071 Then check for any modification hooks in the range.
2072 Create a list of all these hooks in lexicographic order,
2073 eliminating consecutive extra copies of the same hook. Then call
2074 those hooks in order, with START and END - 1 as arguments. */
2077 verify_interval_modification (buf
, start
, end
)
2081 register INTERVAL intervals
= BUF_INTERVALS (buf
);
2082 register INTERVAL i
;
2084 register Lisp_Object prev_mod_hooks
;
2085 Lisp_Object mod_hooks
;
2086 struct gcpro gcpro1
;
2089 prev_mod_hooks
= Qnil
;
2092 interval_insert_behind_hooks
= Qnil
;
2093 interval_insert_in_front_hooks
= Qnil
;
2095 if (NULL_INTERVAL_P (intervals
))
2105 /* For an insert operation, check the two chars around the position. */
2108 INTERVAL prev
= NULL
;
2109 Lisp_Object before
, after
;
2111 /* Set I to the interval containing the char after START,
2112 and PREV to the interval containing the char before START.
2113 Either one may be null. They may be equal. */
2114 i
= find_interval (intervals
, start
);
2116 if (start
== BUF_BEGV (buf
))
2118 else if (i
->position
== start
)
2119 prev
= previous_interval (i
);
2120 else if (i
->position
< start
)
2122 if (start
== BUF_ZV (buf
))
2125 /* If Vinhibit_read_only is set and is not a list, we can
2126 skip the read_only checks. */
2127 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2129 /* If I and PREV differ we need to check for the read-only
2130 property together with its stickiness. If either I or
2131 PREV are 0, this check is all we need.
2132 We have to take special care, since read-only may be
2133 indirectly defined via the category property. */
2136 if (! NULL_INTERVAL_P (i
))
2138 after
= textget (i
->plist
, Qread_only
);
2140 /* If interval I is read-only and read-only is
2141 front-sticky, inhibit insertion.
2142 Check for read-only as well as category. */
2144 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2148 tem
= textget (i
->plist
, Qfront_sticky
);
2149 if (TMEM (Qread_only
, tem
)
2150 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2151 && TMEM (Qcategory
, tem
)))
2152 text_read_only (after
);
2156 if (! NULL_INTERVAL_P (prev
))
2158 before
= textget (prev
->plist
, Qread_only
);
2160 /* If interval PREV is read-only and read-only isn't
2161 rear-nonsticky, inhibit insertion.
2162 Check for read-only as well as category. */
2164 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2168 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2169 if (! TMEM (Qread_only
, tem
)
2170 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2171 || ! TMEM (Qcategory
, tem
)))
2172 text_read_only (before
);
2176 else if (! NULL_INTERVAL_P (i
))
2178 after
= textget (i
->plist
, Qread_only
);
2180 /* If interval I is read-only and read-only is
2181 front-sticky, inhibit insertion.
2182 Check for read-only as well as category. */
2183 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2187 tem
= textget (i
->plist
, Qfront_sticky
);
2188 if (TMEM (Qread_only
, tem
)
2189 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2190 && TMEM (Qcategory
, tem
)))
2191 text_read_only (after
);
2193 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2194 if (! TMEM (Qread_only
, tem
)
2195 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2196 || ! TMEM (Qcategory
, tem
)))
2197 text_read_only (after
);
2202 /* Run both insert hooks (just once if they're the same). */
2203 if (!NULL_INTERVAL_P (prev
))
2204 interval_insert_behind_hooks
2205 = textget (prev
->plist
, Qinsert_behind_hooks
);
2206 if (!NULL_INTERVAL_P (i
))
2207 interval_insert_in_front_hooks
2208 = textget (i
->plist
, Qinsert_in_front_hooks
);
2212 /* Loop over intervals on or next to START...END,
2213 collecting their hooks. */
2215 i
= find_interval (intervals
, start
);
2218 if (! INTERVAL_WRITABLE_P (i
))
2219 text_read_only (textget (i
->plist
, Qread_only
));
2221 if (!inhibit_modification_hooks
)
2223 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2224 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2226 hooks
= Fcons (mod_hooks
, hooks
);
2227 prev_mod_hooks
= mod_hooks
;
2231 i
= next_interval (i
);
2233 /* Keep going thru the interval containing the char before END. */
2234 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2236 if (!inhibit_modification_hooks
)
2239 hooks
= Fnreverse (hooks
);
2240 while (! EQ (hooks
, Qnil
))
2242 call_mod_hooks (Fcar (hooks
), make_number (start
),
2244 hooks
= Fcdr (hooks
);
2251 /* Run the interval hooks for an insertion on character range START ... END.
2252 verify_interval_modification chose which hooks to run;
2253 this function is called after the insertion happens
2254 so it can indicate the range of inserted text. */
2257 report_interval_modification (start
, end
)
2258 Lisp_Object start
, end
;
2260 if (! NILP (interval_insert_behind_hooks
))
2261 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2262 if (! NILP (interval_insert_in_front_hooks
)
2263 && ! EQ (interval_insert_in_front_hooks
,
2264 interval_insert_behind_hooks
))
2265 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2271 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
2272 doc
: /* Property-list used as default values.
2273 The value of a property in this list is seen as the value for every
2274 character that does not have its own value for that property. */);
2275 Vdefault_text_properties
= Qnil
;
2277 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist
,
2278 doc
: /* Alist of alternative properties for properties without a value.
2279 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2280 If a piece of text has no direct value for a particular property, then
2281 this alist is consulted. If that property appears in the alist, then
2282 the first non-nil value from the associated alternative properties is
2284 Vchar_property_alias_alist
= Qnil
;
2286 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
2287 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2288 This also inhibits the use of the `intangible' text property. */);
2289 Vinhibit_point_motion_hooks
= Qnil
;
2291 DEFVAR_LISP ("text-property-default-nonsticky",
2292 &Vtext_property_default_nonsticky
,
2293 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2294 Each element has the form (PROPERTY . NONSTICKINESS).
2296 If a character in a buffer has PROPERTY, new text inserted adjacent to
2297 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2298 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2299 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2300 /* Text property `syntax-table' should be nonsticky by default. */
2301 Vtext_property_default_nonsticky
2302 = Fcons (Fcons (intern ("syntax-table"), Qt
), Qnil
);
2304 staticpro (&interval_insert_behind_hooks
);
2305 staticpro (&interval_insert_in_front_hooks
);
2306 interval_insert_behind_hooks
= Qnil
;
2307 interval_insert_in_front_hooks
= Qnil
;
2310 /* Common attributes one might give text */
2312 staticpro (&Qforeground
);
2313 Qforeground
= intern ("foreground");
2314 staticpro (&Qbackground
);
2315 Qbackground
= intern ("background");
2317 Qfont
= intern ("font");
2318 staticpro (&Qstipple
);
2319 Qstipple
= intern ("stipple");
2320 staticpro (&Qunderline
);
2321 Qunderline
= intern ("underline");
2322 staticpro (&Qread_only
);
2323 Qread_only
= intern ("read-only");
2324 staticpro (&Qinvisible
);
2325 Qinvisible
= intern ("invisible");
2326 staticpro (&Qintangible
);
2327 Qintangible
= intern ("intangible");
2328 staticpro (&Qcategory
);
2329 Qcategory
= intern ("category");
2330 staticpro (&Qlocal_map
);
2331 Qlocal_map
= intern ("local-map");
2332 staticpro (&Qfront_sticky
);
2333 Qfront_sticky
= intern ("front-sticky");
2334 staticpro (&Qrear_nonsticky
);
2335 Qrear_nonsticky
= intern ("rear-nonsticky");
2336 staticpro (&Qmouse_face
);
2337 Qmouse_face
= intern ("mouse-face");
2338 staticpro (&Qminibuffer_prompt
);
2339 Qminibuffer_prompt
= intern ("minibuffer-prompt");
2341 /* Properties that text might use to specify certain actions */
2343 staticpro (&Qmouse_left
);
2344 Qmouse_left
= intern ("mouse-left");
2345 staticpro (&Qmouse_entered
);
2346 Qmouse_entered
= intern ("mouse-entered");
2347 staticpro (&Qpoint_left
);
2348 Qpoint_left
= intern ("point-left");
2349 staticpro (&Qpoint_entered
);
2350 Qpoint_entered
= intern ("point-entered");
2352 defsubr (&Stext_properties_at
);
2353 defsubr (&Sget_text_property
);
2354 defsubr (&Sget_char_property
);
2355 defsubr (&Sget_char_property_and_overlay
);
2356 defsubr (&Snext_char_property_change
);
2357 defsubr (&Sprevious_char_property_change
);
2358 defsubr (&Snext_single_char_property_change
);
2359 defsubr (&Sprevious_single_char_property_change
);
2360 defsubr (&Snext_property_change
);
2361 defsubr (&Snext_single_property_change
);
2362 defsubr (&Sprevious_property_change
);
2363 defsubr (&Sprevious_single_property_change
);
2364 defsubr (&Sadd_text_properties
);
2365 defsubr (&Sput_text_property
);
2366 defsubr (&Sset_text_properties
);
2367 defsubr (&Sremove_text_properties
);
2368 defsubr (&Sremove_list_of_text_properties
);
2369 defsubr (&Stext_property_any
);
2370 defsubr (&Stext_property_not_all
);
2371 /* defsubr (&Serase_text_properties); */
2372 /* defsubr (&Scopy_text_properties); */
2375 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2376 (do not change this comment) */