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, or (at your option)
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
24 #include "intervals.h"
29 #define NULL (void *)0
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
38 /* NOTES: previous- and next- property change will have to skip
39 zero-length intervals if they are implemented. This could be done
40 inside next_interval and previous_interval.
42 set_properties needs to deal with the interval property cache.
44 It is assumed that for any interval plist, a property appears
45 only once on the list. Although some code i.e., remove_properties,
46 handles the more general case, the uniqueness of properties is
47 necessary for the system to remain consistent. This requirement
48 is enforced by the subrs installing properties onto the intervals. */
52 Lisp_Object Qmouse_left
;
53 Lisp_Object Qmouse_entered
;
54 Lisp_Object Qpoint_left
;
55 Lisp_Object Qpoint_entered
;
56 Lisp_Object Qcategory
;
57 Lisp_Object Qlocal_map
;
59 /* Visual properties text (including strings) may have. */
60 Lisp_Object Qforeground
, Qbackground
, Qfont
, Qunderline
, Qstipple
;
61 Lisp_Object Qinvisible
, Qread_only
, Qintangible
, Qmouse_face
;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
71 Lisp_Object Vinhibit_point_motion_hooks
;
72 Lisp_Object Vdefault_text_properties
;
73 Lisp_Object Vchar_property_alias_alist
;
74 Lisp_Object Vtext_property_default_nonsticky
;
76 /* verify_interval_modification saves insertion hooks here
77 to be run later by report_interval_modification. */
78 Lisp_Object interval_insert_behind_hooks
;
79 Lisp_Object interval_insert_in_front_hooks
;
81 static void text_read_only
P_ ((Lisp_Object
)) NO_RETURN
;
84 /* Signal a `text-read-only' error. This function makes it easier
85 to capture that error in GDB by putting a breakpoint on it. */
88 text_read_only (propval
)
91 if (STRINGP (propval
))
92 xsignal1 (Qtext_read_only
, propval
);
94 xsignal0 (Qtext_read_only
);
99 /* Extract the interval at the position pointed to by BEGIN from
100 OBJECT, a string or buffer. Additionally, check that the positions
101 pointed to by BEGIN and END are within the bounds of OBJECT, and
102 reverse them if *BEGIN is greater than *END. The objects pointed
103 to by BEGIN and END may be integers or markers; if the latter, they
104 are coerced to integers.
106 When OBJECT is a string, we increment *BEGIN and *END
107 to make them origin-one.
109 Note that buffer points don't correspond to interval indices.
110 For example, point-max is 1 greater than the index of the last
111 character. This difference is handled in the caller, which uses
112 the validated points to determine a length, and operates on that.
113 Exceptions are Ftext_properties_at, Fnext_property_change, and
114 Fprevious_property_change which call this function with BEGIN == END.
115 Handle this case specially.
117 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
118 create an interval tree for OBJECT if one doesn't exist, provided
119 the object actually contains text. In the current design, if there
120 is no text, there can be no text properties. */
126 validate_interval_range (object
, begin
, end
, force
)
127 Lisp_Object object
, *begin
, *end
;
133 CHECK_STRING_OR_BUFFER (object
);
134 CHECK_NUMBER_COERCE_MARKER (*begin
);
135 CHECK_NUMBER_COERCE_MARKER (*end
);
137 /* If we are asked for a point, but from a subr which operates
138 on a range, then return nothing. */
139 if (EQ (*begin
, *end
) && begin
!= end
)
140 return NULL_INTERVAL
;
142 if (XINT (*begin
) > XINT (*end
))
150 if (BUFFERP (object
))
152 register struct buffer
*b
= XBUFFER (object
);
154 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
155 && XINT (*end
) <= BUF_ZV (b
)))
156 args_out_of_range (*begin
, *end
);
157 i
= BUF_INTERVALS (b
);
159 /* If there's no text, there are no properties. */
160 if (BUF_BEGV (b
) == BUF_ZV (b
))
161 return NULL_INTERVAL
;
163 searchpos
= XINT (*begin
);
167 int len
= SCHARS (object
);
169 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
170 && XINT (*end
) <= len
))
171 args_out_of_range (*begin
, *end
);
172 XSETFASTINT (*begin
, XFASTINT (*begin
));
174 XSETFASTINT (*end
, XFASTINT (*end
));
175 i
= STRING_INTERVALS (object
);
178 return NULL_INTERVAL
;
180 searchpos
= XINT (*begin
);
183 if (NULL_INTERVAL_P (i
))
184 return (force
? create_root_interval (object
) : i
);
186 return find_interval (i
, searchpos
);
189 /* Validate LIST as a property list. If LIST is not a list, then
190 make one consisting of (LIST nil). Otherwise, verify that LIST
191 is even numbered and thus suitable as a plist. */
194 validate_plist (list
)
203 register Lisp_Object tail
;
204 for (i
= 0, tail
= list
; CONSP (tail
); i
++)
210 error ("Odd length text property list");
214 return Fcons (list
, Fcons (Qnil
, Qnil
));
217 /* Return nonzero if interval I has all the properties,
218 with the same values, of list PLIST. */
221 interval_has_all_properties (plist
, i
)
225 register Lisp_Object tail1
, tail2
, sym1
;
228 /* Go through each element of PLIST. */
229 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
234 /* Go through I's plist, looking for sym1 */
235 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
236 if (EQ (sym1
, XCAR (tail2
)))
238 /* Found the same property on both lists. If the
239 values are unequal, return zero. */
240 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
243 /* Property has same value on both lists; go to next one. */
255 /* Return nonzero if the plist of interval I has any of the
256 properties of PLIST, regardless of their values. */
259 interval_has_some_properties (plist
, i
)
263 register Lisp_Object tail1
, tail2
, sym
;
265 /* Go through each element of PLIST. */
266 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
270 /* Go through i's plist, looking for tail1 */
271 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
272 if (EQ (sym
, XCAR (tail2
)))
279 /* Return nonzero if the plist of interval I has any of the
280 property names in LIST, regardless of their values. */
283 interval_has_some_properties_list (list
, i
)
287 register Lisp_Object tail1
, tail2
, sym
;
289 /* Go through each element of LIST. */
290 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
294 /* Go through i's plist, looking for tail1 */
295 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
296 if (EQ (sym
, XCAR (tail2
)))
303 /* Changing the plists of individual intervals. */
305 /* Return the value of PROP in property-list PLIST, or Qunbound if it
308 property_value (plist
, prop
)
309 Lisp_Object plist
, prop
;
313 while (PLIST_ELT_P (plist
, value
))
314 if (EQ (XCAR (plist
), prop
))
317 plist
= XCDR (value
);
322 /* Set the properties of INTERVAL to PROPERTIES,
323 and record undo info for the previous values.
324 OBJECT is the string or buffer that INTERVAL belongs to. */
327 set_properties (properties
, interval
, object
)
328 Lisp_Object properties
, object
;
331 Lisp_Object sym
, value
;
333 if (BUFFERP (object
))
335 /* For each property in the old plist which is missing from PROPERTIES,
336 or has a different value in PROPERTIES, make an undo record. */
337 for (sym
= interval
->plist
;
338 PLIST_ELT_P (sym
, value
);
340 if (! EQ (property_value (properties
, XCAR (sym
)),
343 record_property_change (interval
->position
, LENGTH (interval
),
344 XCAR (sym
), XCAR (value
),
348 /* For each new property that has no value at all in the old plist,
349 make an undo record binding it to nil, so it will be removed. */
350 for (sym
= properties
;
351 PLIST_ELT_P (sym
, value
);
353 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
355 record_property_change (interval
->position
, LENGTH (interval
),
361 /* Store new properties. */
362 interval
->plist
= Fcopy_sequence (properties
);
365 /* Add the properties of PLIST to the interval I, or set
366 the value of I's property to the value of the property on PLIST
367 if they are different.
369 OBJECT should be the string or buffer the interval is in.
371 Return nonzero if this changes I (i.e., if any members of PLIST
372 are actually added to I's plist) */
375 add_properties (plist
, i
, object
)
380 Lisp_Object tail1
, tail2
, sym1
, val1
;
381 register int changed
= 0;
383 struct gcpro gcpro1
, gcpro2
, gcpro3
;
388 /* No need to protect OBJECT, because we can GC only in the case
389 where it is a buffer, and live buffers are always protected.
390 I and its plist are also protected, via OBJECT. */
391 GCPRO3 (tail1
, sym1
, val1
);
393 /* Go through each element of PLIST. */
394 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
397 val1
= Fcar (XCDR (tail1
));
400 /* Go through I's plist, looking for sym1 */
401 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
402 if (EQ (sym1
, XCAR (tail2
)))
404 /* No need to gcpro, because tail2 protects this
405 and it must be a cons cell (we get an error otherwise). */
406 register Lisp_Object this_cdr
;
408 this_cdr
= XCDR (tail2
);
409 /* Found the property. Now check its value. */
412 /* The properties have the same value on both lists.
413 Continue to the next property. */
414 if (EQ (val1
, Fcar (this_cdr
)))
417 /* Record this change in the buffer, for undo purposes. */
418 if (BUFFERP (object
))
420 record_property_change (i
->position
, LENGTH (i
),
421 sym1
, Fcar (this_cdr
), object
);
424 /* I's property has a different value -- change it */
425 Fsetcar (this_cdr
, val1
);
432 /* Record this change in the buffer, for undo purposes. */
433 if (BUFFERP (object
))
435 record_property_change (i
->position
, LENGTH (i
),
438 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
448 /* For any members of PLIST, or LIST,
449 which are properties of I, remove them from I's plist.
450 (If PLIST is non-nil, use that, otherwise use LIST.)
451 OBJECT is the string or buffer containing I. */
454 remove_properties (plist
, list
, i
, object
)
455 Lisp_Object plist
, list
;
459 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
460 register int changed
= 0;
462 /* Nonzero means tail1 is a plist, otherwise it is a list. */
465 current_plist
= i
->plist
;
468 tail1
= plist
, use_plist
= 1;
470 tail1
= list
, use_plist
= 0;
472 /* Go through each element of LIST or PLIST. */
473 while (CONSP (tail1
))
477 /* First, remove the symbol if it's at the head of the list */
478 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
480 if (BUFFERP (object
))
481 record_property_change (i
->position
, LENGTH (i
),
482 sym
, XCAR (XCDR (current_plist
)),
485 current_plist
= XCDR (XCDR (current_plist
));
489 /* Go through I's plist, looking for SYM. */
490 tail2
= current_plist
;
491 while (! NILP (tail2
))
493 register Lisp_Object
this;
494 this = XCDR (XCDR (tail2
));
495 if (CONSP (this) && EQ (sym
, XCAR (this)))
497 if (BUFFERP (object
))
498 record_property_change (i
->position
, LENGTH (i
),
499 sym
, XCAR (XCDR (this)), object
);
501 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
507 /* Advance thru TAIL1 one way or the other. */
508 tail1
= XCDR (tail1
);
509 if (use_plist
&& CONSP (tail1
))
510 tail1
= XCDR (tail1
);
514 i
->plist
= current_plist
;
519 /* Remove all properties from interval I. Return non-zero
520 if this changes the interval. */
534 /* Returns the interval of POSITION in OBJECT.
535 POSITION is BEG-based. */
538 interval_of (position
, object
)
546 XSETBUFFER (object
, current_buffer
);
547 else if (EQ (object
, Qt
))
548 return NULL_INTERVAL
;
550 CHECK_STRING_OR_BUFFER (object
);
552 if (BUFFERP (object
))
554 register struct buffer
*b
= XBUFFER (object
);
558 i
= BUF_INTERVALS (b
);
563 end
= SCHARS (object
);
564 i
= STRING_INTERVALS (object
);
567 if (!(beg
<= position
&& position
<= end
))
568 args_out_of_range (make_number (position
), make_number (position
));
569 if (beg
== end
|| NULL_INTERVAL_P (i
))
570 return NULL_INTERVAL
;
572 return find_interval (i
, position
);
575 DEFUN ("text-properties-at", Ftext_properties_at
,
576 Stext_properties_at
, 1, 2, 0,
577 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
578 If the optional second argument OBJECT is a buffer (or nil, which means
579 the current buffer), POSITION is a buffer position (integer or marker).
580 If OBJECT is a string, POSITION is a 0-based index into it.
581 If POSITION is at the end of OBJECT, the value is nil. */)
583 Lisp_Object position
, object
;
588 XSETBUFFER (object
, current_buffer
);
590 i
= validate_interval_range (object
, &position
, &position
, soft
);
591 if (NULL_INTERVAL_P (i
))
593 /* If POSITION is at the end of the interval,
594 it means it's the end of OBJECT.
595 There are no properties at the very end,
596 since no character follows. */
597 if (XINT (position
) == LENGTH (i
) + i
->position
)
603 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
604 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
605 OBJECT is optional and defaults to the current buffer.
606 If POSITION is at the end of OBJECT, the value is nil. */)
607 (position
, prop
, object
)
608 Lisp_Object position
, object
;
611 return textget (Ftext_properties_at (position
, object
), prop
);
614 /* Return the value of char's property PROP, in OBJECT at POSITION.
615 OBJECT is optional and defaults to the current buffer.
616 If OVERLAY is non-0, then in the case that the returned property is from
617 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
618 returned in *OVERLAY.
619 If POSITION is at the end of OBJECT, the value is nil.
620 If OBJECT is a buffer, then overlay properties are considered as well as
622 If OBJECT is a window, then that window's buffer is used, but
623 window-specific overlays are considered only if they are associated
626 get_char_property_and_overlay (position
, prop
, object
, overlay
)
627 Lisp_Object position
, object
;
628 register Lisp_Object prop
;
629 Lisp_Object
*overlay
;
631 struct window
*w
= 0;
633 CHECK_NUMBER_COERCE_MARKER (position
);
636 XSETBUFFER (object
, current_buffer
);
638 if (WINDOWP (object
))
640 w
= XWINDOW (object
);
643 if (BUFFERP (object
))
646 Lisp_Object
*overlay_vec
;
647 struct buffer
*obuf
= current_buffer
;
649 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
650 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
651 xsignal1 (Qargs_out_of_range
, position
);
653 set_buffer_temp (XBUFFER (object
));
655 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
656 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
658 set_buffer_temp (obuf
);
660 /* Now check the overlays in order of decreasing priority. */
661 while (--noverlays
>= 0)
663 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
667 /* Return the overlay we got the property from. */
668 *overlay
= overlay_vec
[noverlays
];
675 /* Indicate that the return value is not from an overlay. */
678 /* Not a buffer, or no appropriate overlay, so fall through to the
680 return Fget_text_property (position
, prop
, object
);
683 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
684 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
685 Both overlay properties and text properties are checked.
686 OBJECT is optional and defaults to the current buffer.
687 If POSITION is at the end of OBJECT, the value is nil.
688 If OBJECT is a buffer, then overlay properties are considered as well as
690 If OBJECT is a window, then that window's buffer is used, but window-specific
691 overlays are considered only if they are associated with OBJECT. */)
692 (position
, prop
, object
)
693 Lisp_Object position
, object
;
694 register Lisp_Object prop
;
696 return get_char_property_and_overlay (position
, prop
, object
, 0);
699 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
700 Sget_char_property_and_overlay
, 2, 3, 0,
701 doc
: /* Like `get-char-property', but with extra overlay information.
702 The value is a cons cell. Its car is the return value of `get-char-property'
703 with the same arguments--that is, the value of POSITION's property
704 PROP in OBJECT. Its cdr is the overlay in which the property was
705 found, or nil, if it was found as a text property or not found at all.
707 OBJECT is optional and defaults to the current buffer. OBJECT may be
708 a string, a buffer or a window. For strings, the cdr of the return
709 value is always nil, since strings do not have overlays. If OBJECT is
710 a window, then that window's buffer is used, but window-specific
711 overlays are considered only if they are associated with OBJECT. If
712 POSITION is at the end of OBJECT, both car and cdr are nil. */)
713 (position
, prop
, object
)
714 Lisp_Object position
, object
;
715 register Lisp_Object prop
;
719 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
720 return Fcons (val
, overlay
);
724 DEFUN ("next-char-property-change", Fnext_char_property_change
,
725 Snext_char_property_change
, 1, 2, 0,
726 doc
: /* Return the position of next text property or overlay change.
727 This scans characters forward in the current buffer from POSITION till
728 it finds a change in some text property, or the beginning or end of an
729 overlay, and returns the position of that.
730 If none is found up to (point-max), the function returns (point-max).
732 If the optional second argument LIMIT is non-nil, don't search
733 past position LIMIT; return LIMIT if nothing is found before LIMIT.
734 LIMIT is a no-op if it is greater than (point-max). */)
736 Lisp_Object position
, limit
;
740 temp
= Fnext_overlay_change (position
);
743 CHECK_NUMBER_COERCE_MARKER (limit
);
744 if (XINT (limit
) < XINT (temp
))
747 return Fnext_property_change (position
, Qnil
, temp
);
750 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
751 Sprevious_char_property_change
, 1, 2, 0,
752 doc
: /* Return the position of previous text property or overlay change.
753 Scans characters backward in the current buffer from POSITION till it
754 finds a change in some text property, or the beginning or end of an
755 overlay, and returns the position of that.
756 If none is found since (point-min), the function returns (point-min).
758 If the optional second argument LIMIT is non-nil, don't search
759 past position LIMIT; return LIMIT if nothing is found before LIMIT.
760 LIMIT is a no-op if it is less than (point-min). */)
762 Lisp_Object position
, limit
;
766 temp
= Fprevious_overlay_change (position
);
769 CHECK_NUMBER_COERCE_MARKER (limit
);
770 if (XINT (limit
) > XINT (temp
))
773 return Fprevious_property_change (position
, Qnil
, temp
);
777 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
778 Snext_single_char_property_change
, 2, 4, 0,
779 doc
: /* Return the position of next text property or overlay change for a specific property.
780 Scans characters forward from POSITION till it finds
781 a change in the PROP property, then returns the position of the change.
782 If the optional third argument OBJECT is a buffer (or nil, which means
783 the current buffer), POSITION is a buffer position (integer or marker).
784 If OBJECT is a string, POSITION is a 0-based index into it.
786 In a string, scan runs to the end of the string.
787 In a buffer, it runs to (point-max), and the value cannot exceed that.
789 The property values are compared with `eq'.
790 If the property is constant all the way to the end of OBJECT, return the
791 last valid position in OBJECT.
792 If the optional fourth argument LIMIT is non-nil, don't search
793 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
794 (position
, prop
, object
, limit
)
795 Lisp_Object prop
, position
, object
, limit
;
797 if (STRINGP (object
))
799 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
803 position
= make_number (SCHARS (object
));
806 CHECK_NUMBER (limit
);
813 Lisp_Object initial_value
, value
;
814 int count
= SPECPDL_INDEX ();
817 CHECK_BUFFER (object
);
819 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
821 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
822 Fset_buffer (object
);
825 CHECK_NUMBER_COERCE_MARKER (position
);
827 initial_value
= Fget_char_property (position
, prop
, object
);
830 XSETFASTINT (limit
, ZV
);
832 CHECK_NUMBER_COERCE_MARKER (limit
);
834 if (XFASTINT (position
) >= XFASTINT (limit
))
837 if (XFASTINT (position
) > ZV
)
838 XSETFASTINT (position
, ZV
);
843 position
= Fnext_char_property_change (position
, limit
);
844 if (XFASTINT (position
) >= XFASTINT (limit
))
850 value
= Fget_char_property (position
, prop
, object
);
851 if (!EQ (value
, initial_value
))
855 unbind_to (count
, Qnil
);
861 DEFUN ("previous-single-char-property-change",
862 Fprevious_single_char_property_change
,
863 Sprevious_single_char_property_change
, 2, 4, 0,
864 doc
: /* Return the position of previous text property or overlay change for a specific property.
865 Scans characters backward from POSITION till it finds
866 a change in the PROP property, then returns the position of the change.
867 If the optional third argument OBJECT is a buffer (or nil, which means
868 the current buffer), POSITION is a buffer position (integer or marker).
869 If OBJECT is a string, POSITION is a 0-based index into it.
871 In a string, scan runs to the start of the string.
872 In a buffer, it runs to (point-min), and the value cannot be less than that.
874 The property values are compared with `eq'.
875 If the property is constant all the way to the start of OBJECT, return the
876 first valid position in OBJECT.
877 If the optional fourth argument LIMIT is non-nil, don't search
878 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
879 (position
, prop
, object
, limit
)
880 Lisp_Object prop
, position
, object
, limit
;
882 if (STRINGP (object
))
884 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
888 position
= make_number (SCHARS (object
));
891 CHECK_NUMBER (limit
);
898 int count
= SPECPDL_INDEX ();
901 CHECK_BUFFER (object
);
903 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
905 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
906 Fset_buffer (object
);
909 CHECK_NUMBER_COERCE_MARKER (position
);
912 XSETFASTINT (limit
, BEGV
);
914 CHECK_NUMBER_COERCE_MARKER (limit
);
916 if (XFASTINT (position
) <= XFASTINT (limit
))
919 if (XFASTINT (position
) < BEGV
)
920 XSETFASTINT (position
, BEGV
);
924 Lisp_Object initial_value
925 = Fget_char_property (make_number (XFASTINT (position
) - 1),
930 position
= Fprevious_char_property_change (position
, limit
);
932 if (XFASTINT (position
) <= XFASTINT (limit
))
940 = Fget_char_property (make_number (XFASTINT (position
) - 1),
943 if (!EQ (value
, initial_value
))
949 unbind_to (count
, Qnil
);
955 DEFUN ("next-property-change", Fnext_property_change
,
956 Snext_property_change
, 1, 3, 0,
957 doc
: /* Return the position of next property change.
958 Scans characters forward from POSITION in OBJECT till it finds
959 a change in some text property, then returns the position of the change.
960 If the optional second argument OBJECT is a buffer (or nil, which means
961 the current buffer), POSITION is a buffer position (integer or marker).
962 If OBJECT is a string, POSITION is a 0-based index into it.
963 Return nil if the property is constant all the way to the end of OBJECT.
964 If the value is non-nil, it is a position greater than POSITION, never equal.
966 If the optional third argument LIMIT is non-nil, don't search
967 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
968 (position
, object
, limit
)
969 Lisp_Object position
, object
, limit
;
971 register INTERVAL i
, next
;
974 XSETBUFFER (object
, current_buffer
);
976 if (!NILP (limit
) && !EQ (limit
, Qt
))
977 CHECK_NUMBER_COERCE_MARKER (limit
);
979 i
= validate_interval_range (object
, &position
, &position
, soft
);
981 /* If LIMIT is t, return start of next interval--don't
982 bother checking further intervals. */
985 if (NULL_INTERVAL_P (i
))
988 next
= next_interval (i
);
990 if (NULL_INTERVAL_P (next
))
991 XSETFASTINT (position
, (STRINGP (object
)
993 : BUF_ZV (XBUFFER (object
))));
995 XSETFASTINT (position
, next
->position
);
999 if (NULL_INTERVAL_P (i
))
1002 next
= next_interval (i
);
1004 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
1005 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1006 next
= next_interval (next
);
1008 if (NULL_INTERVAL_P (next
)
1010 >= (INTEGERP (limit
)
1014 : BUF_ZV (XBUFFER (object
))))))
1017 return make_number (next
->position
);
1020 /* Return 1 if there's a change in some property between BEG and END. */
1023 property_change_between_p (beg
, end
)
1026 register INTERVAL i
, next
;
1027 Lisp_Object object
, pos
;
1029 XSETBUFFER (object
, current_buffer
);
1030 XSETFASTINT (pos
, beg
);
1032 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
1033 if (NULL_INTERVAL_P (i
))
1036 next
= next_interval (i
);
1037 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
1039 next
= next_interval (next
);
1040 if (NULL_INTERVAL_P (next
))
1042 if (next
->position
>= end
)
1046 if (NULL_INTERVAL_P (next
))
1052 DEFUN ("next-single-property-change", Fnext_single_property_change
,
1053 Snext_single_property_change
, 2, 4, 0,
1054 doc
: /* Return the position of next property change for a specific property.
1055 Scans characters forward from POSITION till it finds
1056 a change in the PROP property, then returns the position of the change.
1057 If the optional third argument OBJECT is a buffer (or nil, which means
1058 the current buffer), POSITION is a buffer position (integer or marker).
1059 If OBJECT is a string, POSITION is a 0-based index into it.
1060 The property values are compared with `eq'.
1061 Return nil if the property is constant all the way to the end of OBJECT.
1062 If the value is non-nil, it is a position greater than POSITION, never equal.
1064 If the optional fourth argument LIMIT is non-nil, don't search
1065 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1066 (position
, prop
, object
, limit
)
1067 Lisp_Object position
, prop
, object
, limit
;
1069 register INTERVAL i
, next
;
1070 register Lisp_Object here_val
;
1073 XSETBUFFER (object
, current_buffer
);
1076 CHECK_NUMBER_COERCE_MARKER (limit
);
1078 i
= validate_interval_range (object
, &position
, &position
, soft
);
1079 if (NULL_INTERVAL_P (i
))
1082 here_val
= textget (i
->plist
, prop
);
1083 next
= next_interval (i
);
1084 while (! NULL_INTERVAL_P (next
)
1085 && EQ (here_val
, textget (next
->plist
, prop
))
1086 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1087 next
= next_interval (next
);
1089 if (NULL_INTERVAL_P (next
)
1091 >= (INTEGERP (limit
)
1095 : BUF_ZV (XBUFFER (object
))))))
1098 return make_number (next
->position
);
1101 DEFUN ("previous-property-change", Fprevious_property_change
,
1102 Sprevious_property_change
, 1, 3, 0,
1103 doc
: /* Return the position of previous property change.
1104 Scans characters backwards from POSITION in OBJECT till it finds
1105 a change in some text property, then returns the position of the change.
1106 If the optional second argument OBJECT is a buffer (or nil, which means
1107 the current buffer), POSITION is a buffer position (integer or marker).
1108 If OBJECT is a string, POSITION is a 0-based index into it.
1109 Return nil if the property is constant all the way to the start of OBJECT.
1110 If the value is non-nil, it is a position less than POSITION, never equal.
1112 If the optional third argument LIMIT is non-nil, don't search
1113 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1114 (position
, object
, limit
)
1115 Lisp_Object position
, object
, limit
;
1117 register INTERVAL i
, previous
;
1120 XSETBUFFER (object
, current_buffer
);
1123 CHECK_NUMBER_COERCE_MARKER (limit
);
1125 i
= validate_interval_range (object
, &position
, &position
, soft
);
1126 if (NULL_INTERVAL_P (i
))
1129 /* Start with the interval containing the char before point. */
1130 if (i
->position
== XFASTINT (position
))
1131 i
= previous_interval (i
);
1133 previous
= previous_interval (i
);
1134 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1136 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1137 previous
= previous_interval (previous
);
1139 if (NULL_INTERVAL_P (previous
)
1140 || (previous
->position
+ LENGTH (previous
)
1141 <= (INTEGERP (limit
)
1143 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1146 return make_number (previous
->position
+ LENGTH (previous
));
1149 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1150 Sprevious_single_property_change
, 2, 4, 0,
1151 doc
: /* Return the position of previous property change for a specific property.
1152 Scans characters backward from POSITION till it finds
1153 a change in the PROP property, then returns the position of the change.
1154 If the optional third argument OBJECT is a buffer (or nil, which means
1155 the current buffer), POSITION is a buffer position (integer or marker).
1156 If OBJECT is a string, POSITION is a 0-based index into it.
1157 The property values are compared with `eq'.
1158 Return nil if the property is constant all the way to the start of OBJECT.
1159 If the value is non-nil, it is a position less than POSITION, never equal.
1161 If the optional fourth argument LIMIT is non-nil, don't search
1162 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1163 (position
, prop
, object
, limit
)
1164 Lisp_Object position
, prop
, object
, limit
;
1166 register INTERVAL i
, previous
;
1167 register Lisp_Object here_val
;
1170 XSETBUFFER (object
, current_buffer
);
1173 CHECK_NUMBER_COERCE_MARKER (limit
);
1175 i
= validate_interval_range (object
, &position
, &position
, soft
);
1177 /* Start with the interval containing the char before point. */
1178 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1179 i
= previous_interval (i
);
1181 if (NULL_INTERVAL_P (i
))
1184 here_val
= textget (i
->plist
, prop
);
1185 previous
= previous_interval (i
);
1186 while (!NULL_INTERVAL_P (previous
)
1187 && EQ (here_val
, textget (previous
->plist
, prop
))
1189 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1190 previous
= previous_interval (previous
);
1192 if (NULL_INTERVAL_P (previous
)
1193 || (previous
->position
+ LENGTH (previous
)
1194 <= (INTEGERP (limit
)
1196 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1199 return make_number (previous
->position
+ LENGTH (previous
));
1202 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1204 DEFUN ("add-text-properties", Fadd_text_properties
,
1205 Sadd_text_properties
, 3, 4, 0,
1206 doc
: /* Add properties to the text from START to END.
1207 The third argument PROPERTIES is a property list
1208 specifying the property values to add. If the optional fourth argument
1209 OBJECT is a buffer (or nil, which means the current buffer),
1210 START and END are buffer positions (integers or markers).
1211 If OBJECT is a string, START and END are 0-based indices into it.
1212 Return t if any property value actually changed, nil otherwise. */)
1213 (start
, end
, properties
, object
)
1214 Lisp_Object start
, end
, properties
, object
;
1216 register INTERVAL i
, unchanged
;
1217 register int s
, len
, modified
= 0;
1218 struct gcpro gcpro1
;
1220 properties
= validate_plist (properties
);
1221 if (NILP (properties
))
1225 XSETBUFFER (object
, current_buffer
);
1227 i
= validate_interval_range (object
, &start
, &end
, hard
);
1228 if (NULL_INTERVAL_P (i
))
1232 len
= XINT (end
) - s
;
1234 /* No need to protect OBJECT, because we GC only if it's a buffer,
1235 and live buffers are always protected. */
1236 GCPRO1 (properties
);
1238 /* If we're not starting on an interval boundary, we have to
1239 split this interval. */
1240 if (i
->position
!= s
)
1242 /* If this interval already has the properties, we can
1244 if (interval_has_all_properties (properties
, i
))
1246 int got
= (LENGTH (i
) - (s
- i
->position
));
1248 RETURN_UNGCPRO (Qnil
);
1250 i
= next_interval (i
);
1255 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1256 copy_properties (unchanged
, i
);
1260 if (BUFFERP (object
))
1261 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1263 /* We are at the beginning of interval I, with LEN chars to scan. */
1269 if (LENGTH (i
) >= len
)
1271 /* We can UNGCPRO safely here, because there will be just
1272 one more chance to gc, in the next call to add_properties,
1273 and after that we will not need PROPERTIES or OBJECT again. */
1276 if (interval_has_all_properties (properties
, i
))
1278 if (BUFFERP (object
))
1279 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1280 XINT (end
) - XINT (start
));
1282 return modified
? Qt
: Qnil
;
1285 if (LENGTH (i
) == len
)
1287 add_properties (properties
, i
, object
);
1288 if (BUFFERP (object
))
1289 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1290 XINT (end
) - XINT (start
));
1294 /* i doesn't have the properties, and goes past the change limit */
1296 i
= split_interval_left (unchanged
, len
);
1297 copy_properties (unchanged
, i
);
1298 add_properties (properties
, i
, object
);
1299 if (BUFFERP (object
))
1300 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1301 XINT (end
) - XINT (start
));
1306 modified
+= add_properties (properties
, i
, object
);
1307 i
= next_interval (i
);
1311 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1313 DEFUN ("put-text-property", Fput_text_property
,
1314 Sput_text_property
, 4, 5, 0,
1315 doc
: /* Set one property of the text from START to END.
1316 The third and fourth arguments PROPERTY and VALUE
1317 specify the property to add.
1318 If the optional fifth argument OBJECT is a buffer (or nil, which means
1319 the current buffer), START and END are buffer positions (integers or
1320 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1321 (start
, end
, property
, value
, object
)
1322 Lisp_Object start
, end
, property
, value
, object
;
1324 Fadd_text_properties (start
, end
,
1325 Fcons (property
, Fcons (value
, Qnil
)),
1330 DEFUN ("set-text-properties", Fset_text_properties
,
1331 Sset_text_properties
, 3, 4, 0,
1332 doc
: /* Completely replace properties of text from START to END.
1333 The third argument PROPERTIES is the new property list.
1334 If the optional fourth argument OBJECT is a buffer (or nil, which means
1335 the current buffer), START and END are buffer positions (integers or
1336 markers). If OBJECT is a string, START and END are 0-based indices into it.
1337 If PROPERTIES is nil, the effect is to remove all properties from
1338 the designated part of OBJECT. */)
1339 (start
, end
, properties
, object
)
1340 Lisp_Object start
, end
, properties
, object
;
1342 return set_text_properties (start
, end
, properties
, object
, Qt
);
1346 /* Replace properties of text from START to END with new list of
1347 properties PROPERTIES. OBJECT is the buffer or string containing
1348 the text. OBJECT nil means use the current buffer.
1349 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1350 is nil if the function _detected_ that it did not replace any
1351 properties, non-nil otherwise. */
1354 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1355 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1357 register INTERVAL i
;
1358 Lisp_Object ostart
, oend
;
1363 properties
= validate_plist (properties
);
1366 XSETBUFFER (object
, current_buffer
);
1368 /* If we want no properties for a whole string,
1369 get rid of its intervals. */
1370 if (NILP (properties
) && STRINGP (object
)
1371 && XFASTINT (start
) == 0
1372 && XFASTINT (end
) == SCHARS (object
))
1374 if (! STRING_INTERVALS (object
))
1377 STRING_SET_INTERVALS (object
, NULL_INTERVAL
);
1381 i
= validate_interval_range (object
, &start
, &end
, soft
);
1383 if (NULL_INTERVAL_P (i
))
1385 /* If buffer has no properties, and we want none, return now. */
1386 if (NILP (properties
))
1389 /* Restore the original START and END values
1390 because validate_interval_range increments them for strings. */
1394 i
= validate_interval_range (object
, &start
, &end
, hard
);
1395 /* This can return if start == end. */
1396 if (NULL_INTERVAL_P (i
))
1400 if (BUFFERP (object
))
1401 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1403 set_text_properties_1 (start
, end
, properties
, object
, i
);
1405 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1406 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1407 XINT (end
) - XINT (start
));
1411 /* Replace properties of text from START to END with new list of
1412 properties PROPERTIES. BUFFER is the buffer containing
1413 the text. This does not obey any hooks.
1414 You can provide the interval that START is located in as I,
1415 or pass NULL for I and this function will find it.
1416 START and END can be in any order. */
1419 set_text_properties_1 (start
, end
, properties
, buffer
, i
)
1420 Lisp_Object start
, end
, properties
, buffer
;
1423 register INTERVAL prev_changed
= NULL_INTERVAL
;
1424 register int s
, len
;
1428 len
= XINT (end
) - s
;
1438 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1440 if (i
->position
!= s
)
1443 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1445 if (LENGTH (i
) > len
)
1447 copy_properties (unchanged
, i
);
1448 i
= split_interval_left (i
, len
);
1449 set_properties (properties
, i
, buffer
);
1453 set_properties (properties
, i
, buffer
);
1455 if (LENGTH (i
) == len
)
1460 i
= next_interval (i
);
1463 /* We are starting at the beginning of an interval, I */
1469 if (LENGTH (i
) >= len
)
1471 if (LENGTH (i
) > len
)
1472 i
= split_interval_left (i
, len
);
1474 /* We have to call set_properties even if we are going to
1475 merge the intervals, so as to make the undo records
1476 and cause redisplay to happen. */
1477 set_properties (properties
, i
, buffer
);
1478 if (!NULL_INTERVAL_P (prev_changed
))
1479 merge_interval_left (i
);
1485 /* We have to call set_properties even if we are going to
1486 merge the intervals, so as to make the undo records
1487 and cause redisplay to happen. */
1488 set_properties (properties
, i
, buffer
);
1489 if (NULL_INTERVAL_P (prev_changed
))
1492 prev_changed
= i
= merge_interval_left (i
);
1494 i
= next_interval (i
);
1498 DEFUN ("remove-text-properties", Fremove_text_properties
,
1499 Sremove_text_properties
, 3, 4, 0,
1500 doc
: /* Remove some properties from text from START to END.
1501 The third argument PROPERTIES is a property list
1502 whose property names specify the properties to remove.
1503 \(The values stored in PROPERTIES are ignored.)
1504 If the optional fourth argument OBJECT is a buffer (or nil, which means
1505 the current buffer), START and END are buffer positions (integers or
1506 markers). If OBJECT is a string, START and END are 0-based indices into it.
1507 Return t if any property was actually removed, nil otherwise.
1509 Use set-text-properties if you want to remove all text properties. */)
1510 (start
, end
, properties
, object
)
1511 Lisp_Object start
, end
, properties
, object
;
1513 register INTERVAL i
, unchanged
;
1514 register int s
, len
, modified
= 0;
1517 XSETBUFFER (object
, current_buffer
);
1519 i
= validate_interval_range (object
, &start
, &end
, soft
);
1520 if (NULL_INTERVAL_P (i
))
1524 len
= XINT (end
) - s
;
1526 if (i
->position
!= s
)
1528 /* No properties on this first interval -- return if
1529 it covers the entire region. */
1530 if (! interval_has_some_properties (properties
, i
))
1532 int got
= (LENGTH (i
) - (s
- i
->position
));
1536 i
= next_interval (i
);
1538 /* Split away the beginning of this interval; what we don't
1543 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1544 copy_properties (unchanged
, i
);
1548 if (BUFFERP (object
))
1549 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1551 /* We are at the beginning of an interval, with len to scan */
1557 if (LENGTH (i
) >= len
)
1559 if (! interval_has_some_properties (properties
, i
))
1560 return modified
? Qt
: Qnil
;
1562 if (LENGTH (i
) == len
)
1564 remove_properties (properties
, Qnil
, i
, object
);
1565 if (BUFFERP (object
))
1566 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1567 XINT (end
) - XINT (start
));
1571 /* i has the properties, and goes past the change limit */
1573 i
= split_interval_left (i
, len
);
1574 copy_properties (unchanged
, i
);
1575 remove_properties (properties
, Qnil
, i
, object
);
1576 if (BUFFERP (object
))
1577 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1578 XINT (end
) - XINT (start
));
1583 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1584 i
= next_interval (i
);
1588 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1589 Sremove_list_of_text_properties
, 3, 4, 0,
1590 doc
: /* Remove some properties from text from START to END.
1591 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1592 If the optional fourth argument OBJECT is a buffer (or nil, which means
1593 the current buffer), START and END are buffer positions (integers or
1594 markers). If OBJECT is a string, START and END are 0-based indices into it.
1595 Return t if any property was actually removed, nil otherwise. */)
1596 (start
, end
, list_of_properties
, object
)
1597 Lisp_Object start
, end
, list_of_properties
, object
;
1599 register INTERVAL i
, unchanged
;
1600 register int s
, len
, modified
= 0;
1601 Lisp_Object properties
;
1602 properties
= list_of_properties
;
1605 XSETBUFFER (object
, current_buffer
);
1607 i
= validate_interval_range (object
, &start
, &end
, soft
);
1608 if (NULL_INTERVAL_P (i
))
1612 len
= XINT (end
) - s
;
1614 if (i
->position
!= s
)
1616 /* No properties on this first interval -- return if
1617 it covers the entire region. */
1618 if (! interval_has_some_properties_list (properties
, i
))
1620 int got
= (LENGTH (i
) - (s
- i
->position
));
1624 i
= next_interval (i
);
1626 /* Split away the beginning of this interval; what we don't
1631 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1632 copy_properties (unchanged
, i
);
1636 /* We are at the beginning of an interval, with len to scan.
1637 The flag `modified' records if changes have been made.
1638 When object is a buffer, we must call modify_region before changes are
1639 made and signal_after_change when we are done.
1640 We call modify_region before calling remove_properties if modified == 0,
1641 and we call signal_after_change before returning if modified != 0. */
1647 if (LENGTH (i
) >= len
)
1649 if (! interval_has_some_properties_list (properties
, i
))
1652 if (BUFFERP (object
))
1653 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1654 XINT (end
) - XINT (start
));
1660 if (LENGTH (i
) == len
)
1662 if (!modified
&& BUFFERP (object
))
1663 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1664 remove_properties (Qnil
, properties
, i
, object
);
1665 if (BUFFERP (object
))
1666 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1667 XINT (end
) - XINT (start
));
1671 /* i has the properties, and goes past the change limit */
1673 i
= split_interval_left (i
, len
);
1674 copy_properties (unchanged
, i
);
1675 if (!modified
&& BUFFERP (object
))
1676 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1677 remove_properties (Qnil
, properties
, i
, object
);
1678 if (BUFFERP (object
))
1679 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1680 XINT (end
) - XINT (start
));
1684 if (interval_has_some_properties_list (properties
, i
))
1686 if (!modified
&& BUFFERP (object
))
1687 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1688 remove_properties (Qnil
, properties
, i
, object
);
1692 i
= next_interval (i
);
1696 DEFUN ("text-property-any", Ftext_property_any
,
1697 Stext_property_any
, 4, 5, 0,
1698 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1699 If so, return the position of the first character whose property PROPERTY
1700 is `eq' to VALUE. Otherwise return nil.
1701 If the optional fifth argument OBJECT is a buffer (or nil, which means
1702 the current buffer), START and END are buffer positions (integers or
1703 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1704 (start
, end
, property
, value
, object
)
1705 Lisp_Object start
, end
, property
, value
, object
;
1707 register INTERVAL i
;
1708 register int e
, pos
;
1711 XSETBUFFER (object
, current_buffer
);
1712 i
= validate_interval_range (object
, &start
, &end
, soft
);
1713 if (NULL_INTERVAL_P (i
))
1714 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1717 while (! NULL_INTERVAL_P (i
))
1719 if (i
->position
>= e
)
1721 if (EQ (textget (i
->plist
, property
), value
))
1724 if (pos
< XINT (start
))
1726 return make_number (pos
);
1728 i
= next_interval (i
);
1733 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1734 Stext_property_not_all
, 4, 5, 0,
1735 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1736 If so, return the position of the first character whose property PROPERTY
1737 is not `eq' to VALUE. Otherwise, return nil.
1738 If the optional fifth argument OBJECT is a buffer (or nil, which means
1739 the current buffer), START and END are buffer positions (integers or
1740 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1741 (start
, end
, property
, value
, object
)
1742 Lisp_Object start
, end
, property
, value
, object
;
1744 register INTERVAL i
;
1748 XSETBUFFER (object
, current_buffer
);
1749 i
= validate_interval_range (object
, &start
, &end
, soft
);
1750 if (NULL_INTERVAL_P (i
))
1751 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1755 while (! NULL_INTERVAL_P (i
))
1757 if (i
->position
>= e
)
1759 if (! EQ (textget (i
->plist
, property
), value
))
1761 if (i
->position
> s
)
1763 return make_number (s
);
1765 i
= next_interval (i
);
1771 /* Return the direction from which the text-property PROP would be
1772 inherited by any new text inserted at POS: 1 if it would be
1773 inherited from the char after POS, -1 if it would be inherited from
1774 the char before POS, and 0 if from neither.
1775 BUFFER can be either a buffer or nil (meaning current buffer). */
1778 text_property_stickiness (prop
, pos
, buffer
)
1779 Lisp_Object prop
, pos
, buffer
;
1781 Lisp_Object prev_pos
, front_sticky
;
1782 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1785 XSETBUFFER (buffer
, current_buffer
);
1787 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1788 /* Consider previous character. */
1790 Lisp_Object rear_non_sticky
;
1792 prev_pos
= make_number (XINT (pos
) - 1);
1793 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1795 if (!NILP (CONSP (rear_non_sticky
)
1796 ? Fmemq (prop
, rear_non_sticky
)
1798 /* PROP is rear-non-sticky. */
1804 /* Consider following character. */
1805 /* This signals an arg-out-of-range error if pos is outside the
1806 buffer's accessible range. */
1807 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1809 if (EQ (front_sticky
, Qt
)
1810 || (CONSP (front_sticky
)
1811 && !NILP (Fmemq (prop
, front_sticky
))))
1812 /* PROP is inherited from after. */
1813 is_front_sticky
= 1;
1815 /* Simple cases, where the properties are consistent. */
1816 if (is_rear_sticky
&& !is_front_sticky
)
1818 else if (!is_rear_sticky
&& is_front_sticky
)
1820 else if (!is_rear_sticky
&& !is_front_sticky
)
1823 /* The stickiness properties are inconsistent, so we have to
1824 disambiguate. Basically, rear-sticky wins, _except_ if the
1825 property that would be inherited has a value of nil, in which case
1826 front-sticky wins. */
1827 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1828 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1835 /* I don't think this is the right interface to export; how often do you
1836 want to do something like this, other than when you're copying objects
1839 I think it would be better to have a pair of functions, one which
1840 returns the text properties of a region as a list of ranges and
1841 plists, and another which applies such a list to another object. */
1843 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1844 SRC and DEST may each refer to strings or buffers.
1845 Optional sixth argument PROP causes only that property to be copied.
1846 Properties are copied to DEST as if by `add-text-properties'.
1847 Return t if any property value actually changed, nil otherwise. */
1849 /* Note this can GC when DEST is a buffer. */
1852 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1853 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1859 int s
, e
, e2
, p
, len
, modified
= 0;
1860 struct gcpro gcpro1
, gcpro2
;
1862 i
= validate_interval_range (src
, &start
, &end
, soft
);
1863 if (NULL_INTERVAL_P (i
))
1866 CHECK_NUMBER_COERCE_MARKER (pos
);
1868 Lisp_Object dest_start
, dest_end
;
1871 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1872 /* Apply this to a copy of pos; it will try to increment its arguments,
1873 which we don't want. */
1874 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1885 e2
= i
->position
+ LENGTH (i
);
1892 while (! NILP (plist
))
1894 if (EQ (Fcar (plist
), prop
))
1896 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1899 plist
= Fcdr (Fcdr (plist
));
1903 /* Must defer modifications to the interval tree in case src
1904 and dest refer to the same string or buffer. */
1905 stuff
= Fcons (Fcons (make_number (p
),
1906 Fcons (make_number (p
+ len
),
1907 Fcons (plist
, Qnil
))),
1911 i
= next_interval (i
);
1912 if (NULL_INTERVAL_P (i
))
1919 GCPRO2 (stuff
, dest
);
1921 while (! NILP (stuff
))
1924 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1925 Fcar (Fcdr (Fcdr (res
))), dest
);
1928 stuff
= Fcdr (stuff
);
1933 return modified
? Qt
: Qnil
;
1937 /* Return a list representing the text properties of OBJECT between
1938 START and END. if PROP is non-nil, report only on that property.
1939 Each result list element has the form (S E PLIST), where S and E
1940 are positions in OBJECT and PLIST is a property list containing the
1941 text properties of OBJECT between S and E. Value is nil if OBJECT
1942 doesn't contain text properties between START and END. */
1945 text_property_list (object
, start
, end
, prop
)
1946 Lisp_Object object
, start
, end
, prop
;
1953 i
= validate_interval_range (object
, &start
, &end
, soft
);
1954 if (!NULL_INTERVAL_P (i
))
1956 int s
= XINT (start
);
1961 int interval_end
, len
;
1964 interval_end
= i
->position
+ LENGTH (i
);
1965 if (interval_end
> e
)
1967 len
= interval_end
- s
;
1972 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1973 if (EQ (XCAR (plist
), prop
))
1975 plist
= Fcons (prop
, Fcons (Fcar (XCDR (plist
)), Qnil
));
1980 result
= Fcons (Fcons (make_number (s
),
1981 Fcons (make_number (s
+ len
),
1982 Fcons (plist
, Qnil
))),
1985 i
= next_interval (i
);
1986 if (NULL_INTERVAL_P (i
))
1996 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1997 (START END PLIST), where START and END are positions and PLIST is a
1998 property list containing the text properties to add. Adjust START
1999 and END positions by DELTA before adding properties. Value is
2000 non-zero if OBJECT was modified. */
2003 add_text_properties_from_list (object
, list
, delta
)
2004 Lisp_Object object
, list
, delta
;
2006 struct gcpro gcpro1
, gcpro2
;
2009 GCPRO2 (list
, object
);
2011 for (; CONSP (list
); list
= XCDR (list
))
2013 Lisp_Object item
, start
, end
, plist
, tem
;
2016 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
2017 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
2018 plist
= XCAR (XCDR (XCDR (item
)));
2020 tem
= Fadd_text_properties (start
, end
, plist
, object
);
2031 /* Modify end-points of ranges in LIST destructively. LIST is a list
2032 as returned from text_property_list. Change end-points equal to
2033 OLD_END to NEW_END. */
2036 extend_property_ranges (list
, old_end
, new_end
)
2037 Lisp_Object list
, old_end
, new_end
;
2039 for (; CONSP (list
); list
= XCDR (list
))
2041 Lisp_Object item
, end
;
2044 end
= XCAR (XCDR (item
));
2046 if (EQ (end
, old_end
))
2047 XSETCAR (XCDR (item
), new_end
);
2053 /* Call the modification hook functions in LIST, each with START and END. */
2056 call_mod_hooks (list
, start
, end
)
2057 Lisp_Object list
, start
, end
;
2059 struct gcpro gcpro1
;
2061 while (!NILP (list
))
2063 call2 (Fcar (list
), start
, end
);
2069 /* Check for read-only intervals between character positions START ... END,
2070 in BUF, and signal an error if we find one.
2072 Then check for any modification hooks in the range.
2073 Create a list of all these hooks in lexicographic order,
2074 eliminating consecutive extra copies of the same hook. Then call
2075 those hooks in order, with START and END - 1 as arguments. */
2078 verify_interval_modification (buf
, start
, end
)
2082 register INTERVAL intervals
= BUF_INTERVALS (buf
);
2083 register INTERVAL i
;
2085 register Lisp_Object prev_mod_hooks
;
2086 Lisp_Object mod_hooks
;
2087 struct gcpro gcpro1
;
2090 prev_mod_hooks
= Qnil
;
2093 interval_insert_behind_hooks
= Qnil
;
2094 interval_insert_in_front_hooks
= Qnil
;
2096 if (NULL_INTERVAL_P (intervals
))
2106 /* For an insert operation, check the two chars around the position. */
2109 INTERVAL prev
= NULL
;
2110 Lisp_Object before
, after
;
2112 /* Set I to the interval containing the char after START,
2113 and PREV to the interval containing the char before START.
2114 Either one may be null. They may be equal. */
2115 i
= find_interval (intervals
, start
);
2117 if (start
== BUF_BEGV (buf
))
2119 else if (i
->position
== start
)
2120 prev
= previous_interval (i
);
2121 else if (i
->position
< start
)
2123 if (start
== BUF_ZV (buf
))
2126 /* If Vinhibit_read_only is set and is not a list, we can
2127 skip the read_only checks. */
2128 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2130 /* If I and PREV differ we need to check for the read-only
2131 property together with its stickiness. If either I or
2132 PREV are 0, this check is all we need.
2133 We have to take special care, since read-only may be
2134 indirectly defined via the category property. */
2137 if (! NULL_INTERVAL_P (i
))
2139 after
= textget (i
->plist
, Qread_only
);
2141 /* If interval I is read-only and read-only is
2142 front-sticky, inhibit insertion.
2143 Check for read-only as well as category. */
2145 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2149 tem
= textget (i
->plist
, Qfront_sticky
);
2150 if (TMEM (Qread_only
, tem
)
2151 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2152 && TMEM (Qcategory
, tem
)))
2153 text_read_only (after
);
2157 if (! NULL_INTERVAL_P (prev
))
2159 before
= textget (prev
->plist
, Qread_only
);
2161 /* If interval PREV is read-only and read-only isn't
2162 rear-nonsticky, inhibit insertion.
2163 Check for read-only as well as category. */
2165 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2169 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2170 if (! TMEM (Qread_only
, tem
)
2171 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2172 || ! TMEM (Qcategory
, tem
)))
2173 text_read_only (before
);
2177 else if (! NULL_INTERVAL_P (i
))
2179 after
= textget (i
->plist
, Qread_only
);
2181 /* If interval I is read-only and read-only is
2182 front-sticky, inhibit insertion.
2183 Check for read-only as well as category. */
2184 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2188 tem
= textget (i
->plist
, Qfront_sticky
);
2189 if (TMEM (Qread_only
, tem
)
2190 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2191 && TMEM (Qcategory
, tem
)))
2192 text_read_only (after
);
2194 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2195 if (! TMEM (Qread_only
, tem
)
2196 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2197 || ! TMEM (Qcategory
, tem
)))
2198 text_read_only (after
);
2203 /* Run both insert hooks (just once if they're the same). */
2204 if (!NULL_INTERVAL_P (prev
))
2205 interval_insert_behind_hooks
2206 = textget (prev
->plist
, Qinsert_behind_hooks
);
2207 if (!NULL_INTERVAL_P (i
))
2208 interval_insert_in_front_hooks
2209 = textget (i
->plist
, Qinsert_in_front_hooks
);
2213 /* Loop over intervals on or next to START...END,
2214 collecting their hooks. */
2216 i
= find_interval (intervals
, start
);
2219 if (! INTERVAL_WRITABLE_P (i
))
2220 text_read_only (textget (i
->plist
, Qread_only
));
2222 if (!inhibit_modification_hooks
)
2224 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2225 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2227 hooks
= Fcons (mod_hooks
, hooks
);
2228 prev_mod_hooks
= mod_hooks
;
2232 i
= next_interval (i
);
2234 /* Keep going thru the interval containing the char before END. */
2235 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2237 if (!inhibit_modification_hooks
)
2240 hooks
= Fnreverse (hooks
);
2241 while (! EQ (hooks
, Qnil
))
2243 call_mod_hooks (Fcar (hooks
), make_number (start
),
2245 hooks
= Fcdr (hooks
);
2252 /* Run the interval hooks for an insertion on character range START ... END.
2253 verify_interval_modification chose which hooks to run;
2254 this function is called after the insertion happens
2255 so it can indicate the range of inserted text. */
2258 report_interval_modification (start
, end
)
2259 Lisp_Object start
, end
;
2261 if (! NILP (interval_insert_behind_hooks
))
2262 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2263 if (! NILP (interval_insert_in_front_hooks
)
2264 && ! EQ (interval_insert_in_front_hooks
,
2265 interval_insert_behind_hooks
))
2266 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2272 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
2273 doc
: /* Property-list used as default values.
2274 The value of a property in this list is seen as the value for every
2275 character that does not have its own value for that property. */);
2276 Vdefault_text_properties
= Qnil
;
2278 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist
,
2279 doc
: /* Alist of alternative properties for properties without a value.
2280 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2281 If a piece of text has no direct value for a particular property, then
2282 this alist is consulted. If that property appears in the alist, then
2283 the first non-nil value from the associated alternative properties is
2285 Vchar_property_alias_alist
= Qnil
;
2287 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
2288 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2289 This also inhibits the use of the `intangible' text property. */);
2290 Vinhibit_point_motion_hooks
= Qnil
;
2292 DEFVAR_LISP ("text-property-default-nonsticky",
2293 &Vtext_property_default_nonsticky
,
2294 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2295 Each element has the form (PROPERTY . NONSTICKINESS).
2297 If a character in a buffer has PROPERTY, new text inserted adjacent to
2298 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2299 inherits it if NONSTICKINESS is nil. The front-sticky and
2300 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2301 /* Text property `syntax-table' should be nonsticky by default. */
2302 Vtext_property_default_nonsticky
2303 = Fcons (Fcons (intern ("syntax-table"), Qt
), Qnil
);
2305 staticpro (&interval_insert_behind_hooks
);
2306 staticpro (&interval_insert_in_front_hooks
);
2307 interval_insert_behind_hooks
= Qnil
;
2308 interval_insert_in_front_hooks
= Qnil
;
2311 /* Common attributes one might give text */
2313 staticpro (&Qforeground
);
2314 Qforeground
= intern ("foreground");
2315 staticpro (&Qbackground
);
2316 Qbackground
= intern ("background");
2318 Qfont
= intern ("font");
2319 staticpro (&Qstipple
);
2320 Qstipple
= intern ("stipple");
2321 staticpro (&Qunderline
);
2322 Qunderline
= intern ("underline");
2323 staticpro (&Qread_only
);
2324 Qread_only
= intern ("read-only");
2325 staticpro (&Qinvisible
);
2326 Qinvisible
= intern ("invisible");
2327 staticpro (&Qintangible
);
2328 Qintangible
= intern ("intangible");
2329 staticpro (&Qcategory
);
2330 Qcategory
= intern ("category");
2331 staticpro (&Qlocal_map
);
2332 Qlocal_map
= intern ("local-map");
2333 staticpro (&Qfront_sticky
);
2334 Qfront_sticky
= intern ("front-sticky");
2335 staticpro (&Qrear_nonsticky
);
2336 Qrear_nonsticky
= intern ("rear-nonsticky");
2337 staticpro (&Qmouse_face
);
2338 Qmouse_face
= intern ("mouse-face");
2340 /* Properties that text might use to specify certain actions */
2342 staticpro (&Qmouse_left
);
2343 Qmouse_left
= intern ("mouse-left");
2344 staticpro (&Qmouse_entered
);
2345 Qmouse_entered
= intern ("mouse-entered");
2346 staticpro (&Qpoint_left
);
2347 Qpoint_left
= intern ("point-left");
2348 staticpro (&Qpoint_entered
);
2349 Qpoint_entered
= intern ("point-entered");
2351 defsubr (&Stext_properties_at
);
2352 defsubr (&Sget_text_property
);
2353 defsubr (&Sget_char_property
);
2354 defsubr (&Sget_char_property_and_overlay
);
2355 defsubr (&Snext_char_property_change
);
2356 defsubr (&Sprevious_char_property_change
);
2357 defsubr (&Snext_single_char_property_change
);
2358 defsubr (&Sprevious_single_char_property_change
);
2359 defsubr (&Snext_property_change
);
2360 defsubr (&Snext_single_property_change
);
2361 defsubr (&Sprevious_property_change
);
2362 defsubr (&Sprevious_single_property_change
);
2363 defsubr (&Sadd_text_properties
);
2364 defsubr (&Sput_text_property
);
2365 defsubr (&Sset_text_properties
);
2366 defsubr (&Sremove_text_properties
);
2367 defsubr (&Sremove_list_of_text_properties
);
2368 defsubr (&Stext_property_any
);
2369 defsubr (&Stext_property_not_all
);
2370 /* defsubr (&Serase_text_properties); */
2371 /* defsubr (&Scopy_text_properties); */
2374 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2375 (do not change this comment) */