1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
24 #include "intervals.h"
29 #define NULL (void *)0
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
38 /* NOTES: previous- and next- property change will have to skip
39 zero-length intervals if they are implemented. This could be done
40 inside next_interval and previous_interval.
42 set_properties needs to deal with the interval property cache.
44 It is assumed that for any interval plist, a property appears
45 only once on the list. Although some code i.e., remove_properties,
46 handles the more general case, the uniqueness of properties is
47 necessary for the system to remain consistent. This requirement
48 is enforced by the subrs installing properties onto the intervals. */
52 Lisp_Object Qmouse_left
;
53 Lisp_Object Qmouse_entered
;
54 Lisp_Object Qpoint_left
;
55 Lisp_Object Qpoint_entered
;
56 Lisp_Object Qcategory
;
57 Lisp_Object Qlocal_map
;
59 /* Visual properties text (including strings) may have. */
60 Lisp_Object Qforeground
, Qbackground
, Qfont
, Qunderline
, Qstipple
;
61 Lisp_Object Qinvisible
, Qread_only
, Qintangible
, Qmouse_face
;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
71 Lisp_Object Vinhibit_point_motion_hooks
;
72 Lisp_Object Vdefault_text_properties
;
73 Lisp_Object Vchar_property_alias_alist
;
74 Lisp_Object Vtext_property_default_nonsticky
;
76 /* verify_interval_modification saves insertion hooks here
77 to be run later by report_interval_modification. */
78 Lisp_Object interval_insert_behind_hooks
;
79 Lisp_Object interval_insert_in_front_hooks
;
82 /* Signal a `text-read-only' error. This function makes it easier
83 to capture that error in GDB by putting a breakpoint on it. */
86 text_read_only (propval
)
89 Fsignal (Qtext_read_only
, STRINGP (propval
) ? Fcons (propval
, Qnil
) : Qnil
);
94 /* Extract the interval at the position pointed to by BEGIN from
95 OBJECT, a string or buffer. Additionally, check that the positions
96 pointed to by BEGIN and END are within the bounds of OBJECT, and
97 reverse them if *BEGIN is greater than *END. The objects pointed
98 to by BEGIN and END may be integers or markers; if the latter, they
99 are coerced to integers.
101 When OBJECT is a string, we increment *BEGIN and *END
102 to make them origin-one.
104 Note that buffer points don't correspond to interval indices.
105 For example, point-max is 1 greater than the index of the last
106 character. This difference is handled in the caller, which uses
107 the validated points to determine a length, and operates on that.
108 Exceptions are Ftext_properties_at, Fnext_property_change, and
109 Fprevious_property_change which call this function with BEGIN == END.
110 Handle this case specially.
112 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
113 create an interval tree for OBJECT if one doesn't exist, provided
114 the object actually contains text. In the current design, if there
115 is no text, there can be no text properties. */
121 validate_interval_range (object
, begin
, end
, force
)
122 Lisp_Object object
, *begin
, *end
;
128 CHECK_STRING_OR_BUFFER (object
);
129 CHECK_NUMBER_COERCE_MARKER (*begin
);
130 CHECK_NUMBER_COERCE_MARKER (*end
);
132 /* If we are asked for a point, but from a subr which operates
133 on a range, then return nothing. */
134 if (EQ (*begin
, *end
) && begin
!= end
)
135 return NULL_INTERVAL
;
137 if (XINT (*begin
) > XINT (*end
))
145 if (BUFFERP (object
))
147 register struct buffer
*b
= XBUFFER (object
);
149 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
150 && XINT (*end
) <= BUF_ZV (b
)))
151 args_out_of_range (*begin
, *end
);
152 i
= BUF_INTERVALS (b
);
154 /* If there's no text, there are no properties. */
155 if (BUF_BEGV (b
) == BUF_ZV (b
))
156 return NULL_INTERVAL
;
158 searchpos
= XINT (*begin
);
162 int len
= SCHARS (object
);
164 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
165 && XINT (*end
) <= len
))
166 args_out_of_range (*begin
, *end
);
167 XSETFASTINT (*begin
, XFASTINT (*begin
));
169 XSETFASTINT (*end
, XFASTINT (*end
));
170 i
= STRING_INTERVALS (object
);
173 return NULL_INTERVAL
;
175 searchpos
= XINT (*begin
);
178 if (NULL_INTERVAL_P (i
))
179 return (force
? create_root_interval (object
) : i
);
181 return find_interval (i
, searchpos
);
184 /* Validate LIST as a property list. If LIST is not a list, then
185 make one consisting of (LIST nil). Otherwise, verify that LIST
186 is even numbered and thus suitable as a plist. */
189 validate_plist (list
)
198 register Lisp_Object tail
;
199 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
205 error ("Odd length text property list");
209 return Fcons (list
, Fcons (Qnil
, Qnil
));
212 /* Return nonzero if interval I has all the properties,
213 with the same values, of list PLIST. */
216 interval_has_all_properties (plist
, i
)
220 register Lisp_Object tail1
, tail2
, sym1
;
223 /* Go through each element of PLIST. */
224 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
229 /* Go through I's plist, looking for sym1 */
230 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
231 if (EQ (sym1
, Fcar (tail2
)))
233 /* Found the same property on both lists. If the
234 values are unequal, return zero. */
235 if (! EQ (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))))
238 /* Property has same value on both lists; go to next one. */
250 /* Return nonzero if the plist of interval I has any of the
251 properties of PLIST, regardless of their values. */
254 interval_has_some_properties (plist
, i
)
258 register Lisp_Object tail1
, tail2
, sym
;
260 /* Go through each element of PLIST. */
261 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
265 /* Go through i's plist, looking for tail1 */
266 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
267 if (EQ (sym
, Fcar (tail2
)))
274 /* Return nonzero if the plist of interval I has any of the
275 property names in LIST, regardless of their values. */
278 interval_has_some_properties_list (list
, i
)
282 register Lisp_Object tail1
, tail2
, sym
;
284 /* Go through each element of LIST. */
285 for (tail1
= list
; ! NILP (tail1
); tail1
= XCDR (tail1
))
289 /* Go through i's plist, looking for tail1 */
290 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= XCDR (XCDR (tail2
)))
291 if (EQ (sym
, XCAR (tail2
)))
298 /* Changing the plists of individual intervals. */
300 /* Return the value of PROP in property-list PLIST, or Qunbound if it
303 property_value (plist
, prop
)
304 Lisp_Object plist
, prop
;
308 while (PLIST_ELT_P (plist
, value
))
309 if (EQ (XCAR (plist
), prop
))
312 plist
= XCDR (value
);
317 /* Set the properties of INTERVAL to PROPERTIES,
318 and record undo info for the previous values.
319 OBJECT is the string or buffer that INTERVAL belongs to. */
322 set_properties (properties
, interval
, object
)
323 Lisp_Object properties
, object
;
326 Lisp_Object sym
, value
;
328 if (BUFFERP (object
))
330 /* For each property in the old plist which is missing from PROPERTIES,
331 or has a different value in PROPERTIES, make an undo record. */
332 for (sym
= interval
->plist
;
333 PLIST_ELT_P (sym
, value
);
335 if (! EQ (property_value (properties
, XCAR (sym
)),
338 record_property_change (interval
->position
, LENGTH (interval
),
339 XCAR (sym
), XCAR (value
),
343 /* For each new property that has no value at all in the old plist,
344 make an undo record binding it to nil, so it will be removed. */
345 for (sym
= properties
;
346 PLIST_ELT_P (sym
, value
);
348 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
350 record_property_change (interval
->position
, LENGTH (interval
),
356 /* Store new properties. */
357 interval
->plist
= Fcopy_sequence (properties
);
360 /* Add the properties of PLIST to the interval I, or set
361 the value of I's property to the value of the property on PLIST
362 if they are different.
364 OBJECT should be the string or buffer the interval is in.
366 Return nonzero if this changes I (i.e., if any members of PLIST
367 are actually added to I's plist) */
370 add_properties (plist
, i
, object
)
375 Lisp_Object tail1
, tail2
, sym1
, val1
;
376 register int changed
= 0;
378 struct gcpro gcpro1
, gcpro2
, gcpro3
;
383 /* No need to protect OBJECT, because we can GC only in the case
384 where it is a buffer, and live buffers are always protected.
385 I and its plist are also protected, via OBJECT. */
386 GCPRO3 (tail1
, sym1
, val1
);
388 /* Go through each element of PLIST. */
389 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
392 val1
= Fcar (Fcdr (tail1
));
395 /* Go through I's plist, looking for sym1 */
396 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
397 if (EQ (sym1
, Fcar (tail2
)))
399 /* No need to gcpro, because tail2 protects this
400 and it must be a cons cell (we get an error otherwise). */
401 register Lisp_Object this_cdr
;
403 this_cdr
= Fcdr (tail2
);
404 /* Found the property. Now check its value. */
407 /* The properties have the same value on both lists.
408 Continue to the next property. */
409 if (EQ (val1
, Fcar (this_cdr
)))
412 /* Record this change in the buffer, for undo purposes. */
413 if (BUFFERP (object
))
415 record_property_change (i
->position
, LENGTH (i
),
416 sym1
, Fcar (this_cdr
), object
);
419 /* I's property has a different value -- change it */
420 Fsetcar (this_cdr
, val1
);
427 /* Record this change in the buffer, for undo purposes. */
428 if (BUFFERP (object
))
430 record_property_change (i
->position
, LENGTH (i
),
433 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
443 /* For any members of PLIST, or LIST,
444 which are properties of I, remove them from I's plist.
445 (If PLIST is non-nil, use that, otherwise use LIST.)
446 OBJECT is the string or buffer containing I. */
449 remove_properties (plist
, list
, i
, object
)
450 Lisp_Object plist
, list
;
454 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
455 register int changed
= 0;
457 /* Nonzero means tail1 is a plist, otherwise it is a list. */
460 current_plist
= i
->plist
;
463 tail1
= plist
, use_plist
= 1;
465 tail1
= list
, use_plist
= 0;
467 /* Go through each element of LIST or PLIST. */
468 while (CONSP (tail1
))
472 /* First, remove the symbol if it's at the head of the list */
473 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
475 if (BUFFERP (object
))
476 record_property_change (i
->position
, LENGTH (i
),
477 sym
, XCAR (XCDR (current_plist
)),
480 current_plist
= XCDR (XCDR (current_plist
));
484 /* Go through I's plist, looking for SYM. */
485 tail2
= current_plist
;
486 while (! NILP (tail2
))
488 register Lisp_Object
this;
489 this = XCDR (XCDR (tail2
));
490 if (CONSP (this) && EQ (sym
, XCAR (this)))
492 if (BUFFERP (object
))
493 record_property_change (i
->position
, LENGTH (i
),
494 sym
, XCAR (XCDR (this)), object
);
496 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
502 /* Advance thru TAIL1 one way or the other. */
503 tail1
= XCDR (tail1
);
504 if (use_plist
&& CONSP (tail1
))
505 tail1
= XCDR (tail1
);
509 i
->plist
= current_plist
;
514 /* Remove all properties from interval I. Return non-zero
515 if this changes the interval. */
529 /* Returns the interval of POSITION in OBJECT.
530 POSITION is BEG-based. */
533 interval_of (position
, object
)
541 XSETBUFFER (object
, current_buffer
);
542 else if (EQ (object
, Qt
))
543 return NULL_INTERVAL
;
545 CHECK_STRING_OR_BUFFER (object
);
547 if (BUFFERP (object
))
549 register struct buffer
*b
= XBUFFER (object
);
553 i
= BUF_INTERVALS (b
);
558 end
= SCHARS (object
);
559 i
= STRING_INTERVALS (object
);
562 if (!(beg
<= position
&& position
<= end
))
563 args_out_of_range (make_number (position
), make_number (position
));
564 if (beg
== end
|| NULL_INTERVAL_P (i
))
565 return NULL_INTERVAL
;
567 return find_interval (i
, position
);
570 DEFUN ("text-properties-at", Ftext_properties_at
,
571 Stext_properties_at
, 1, 2, 0,
572 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
573 OBJECT is the string or buffer to look for the properties in;
574 nil means the current buffer.
575 If POSITION is at the end of OBJECT, the value is nil. */)
577 Lisp_Object position
, object
;
582 XSETBUFFER (object
, current_buffer
);
584 i
= validate_interval_range (object
, &position
, &position
, soft
);
585 if (NULL_INTERVAL_P (i
))
587 /* If POSITION is at the end of the interval,
588 it means it's the end of OBJECT.
589 There are no properties at the very end,
590 since no character follows. */
591 if (XINT (position
) == LENGTH (i
) + i
->position
)
597 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
598 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
599 OBJECT is optional and defaults to the current buffer.
600 If POSITION is at the end of OBJECT, the value is nil. */)
601 (position
, prop
, object
)
602 Lisp_Object position
, object
;
605 return textget (Ftext_properties_at (position
, object
), prop
);
608 /* Return the value of char's property PROP, in OBJECT at POSITION.
609 OBJECT is optional and defaults to the current buffer.
610 If OVERLAY is non-0, then in the case that the returned property is from
611 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
612 returned in *OVERLAY.
613 If POSITION is at the end of OBJECT, the value is nil.
614 If OBJECT is a buffer, then overlay properties are considered as well as
616 If OBJECT is a window, then that window's buffer is used, but
617 window-specific overlays are considered only if they are associated
620 get_char_property_and_overlay (position
, prop
, object
, overlay
)
621 Lisp_Object position
, object
;
622 register Lisp_Object prop
;
623 Lisp_Object
*overlay
;
625 struct window
*w
= 0;
627 CHECK_NUMBER_COERCE_MARKER (position
);
630 XSETBUFFER (object
, current_buffer
);
632 if (WINDOWP (object
))
634 w
= XWINDOW (object
);
637 if (BUFFERP (object
))
639 int posn
= XINT (position
);
641 Lisp_Object
*overlay_vec
, tem
;
643 struct buffer
*obuf
= current_buffer
;
645 set_buffer_temp (XBUFFER (object
));
647 /* First try with room for 40 overlays. */
649 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
651 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
654 /* If there are more than 40,
655 make enough space for all, and try again. */
659 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
660 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
663 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
665 set_buffer_temp (obuf
);
667 /* Now check the overlays in order of decreasing priority. */
668 while (--noverlays
>= 0)
670 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
674 /* Return the overlay we got the property from. */
675 *overlay
= overlay_vec
[noverlays
];
682 /* Indicate that the return value is not from an overlay. */
685 /* Not a buffer, or no appropriate overlay, so fall through to the
687 return Fget_text_property (position
, prop
, object
);
690 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
691 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
692 Both overlay properties and text properties are checked.
693 OBJECT is optional and defaults to the current buffer.
694 If POSITION is at the end of OBJECT, the value is nil.
695 If OBJECT is a buffer, then overlay properties are considered as well as
697 If OBJECT is a window, then that window's buffer is used, but window-specific
698 overlays are considered only if they are associated with OBJECT. */)
699 (position
, prop
, object
)
700 Lisp_Object position
, object
;
701 register Lisp_Object prop
;
703 return get_char_property_and_overlay (position
, prop
, object
, 0);
706 DEFUN ("next-char-property-change", Fnext_char_property_change
,
707 Snext_char_property_change
, 1, 2, 0,
708 doc
: /* Return the position of next text property or overlay change.
709 This scans characters forward from POSITION till it finds a change in
710 some text property, or the beginning or end of an overlay, and returns
711 the position of that.
712 If none is found, the function returns (point-max).
714 If the optional third argument LIMIT is non-nil, don't search
715 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
717 Lisp_Object position
, limit
;
721 temp
= Fnext_overlay_change (position
);
724 CHECK_NUMBER (limit
);
725 if (XINT (limit
) < XINT (temp
))
728 return Fnext_property_change (position
, Qnil
, temp
);
731 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
732 Sprevious_char_property_change
, 1, 2, 0,
733 doc
: /* Return the position of previous text property or overlay change.
734 Scans characters backward from POSITION till it finds a change in some
735 text property, or the beginning or end of an overlay, and returns the
737 If none is found, the function returns (point-max).
739 If the optional third argument LIMIT is non-nil, don't search
740 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
742 Lisp_Object position
, limit
;
746 temp
= Fprevious_overlay_change (position
);
749 CHECK_NUMBER (limit
);
750 if (XINT (limit
) > XINT (temp
))
753 return Fprevious_property_change (position
, Qnil
, temp
);
757 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
758 Snext_single_char_property_change
, 2, 4, 0,
759 doc
: /* Return the position of next text property or overlay change for a specific property.
760 Scans characters forward from POSITION till it finds
761 a change in the PROP property, then returns the position of the change.
762 The optional third argument OBJECT is the string or buffer to scan.
763 The property values are compared with `eq'.
764 If the property is constant all the way to the end of OBJECT, return the
765 last valid position in OBJECT.
766 If the optional fourth argument LIMIT is non-nil, don't search
767 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
768 (position
, prop
, object
, limit
)
769 Lisp_Object prop
, position
, object
, limit
;
771 if (STRINGP (object
))
773 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
777 position
= make_number (SCHARS (object
));
784 Lisp_Object initial_value
, value
;
785 int count
= SPECPDL_INDEX ();
788 CHECK_BUFFER (object
);
790 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
792 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
793 Fset_buffer (object
);
796 initial_value
= Fget_char_property (position
, prop
, object
);
799 XSETFASTINT (limit
, BUF_ZV (current_buffer
));
801 CHECK_NUMBER_COERCE_MARKER (limit
);
805 position
= Fnext_char_property_change (position
, limit
);
806 if (XFASTINT (position
) >= XFASTINT (limit
)) {
811 value
= Fget_char_property (position
, prop
, object
);
812 if (!EQ (value
, initial_value
))
816 unbind_to (count
, Qnil
);
822 DEFUN ("previous-single-char-property-change",
823 Fprevious_single_char_property_change
,
824 Sprevious_single_char_property_change
, 2, 4, 0,
825 doc
: /* Return the position of previous text property or overlay change for a specific property.
826 Scans characters backward from POSITION till it finds
827 a change in the PROP property, then returns the position of the change.
828 The optional third argument OBJECT is the string or buffer to scan.
829 The property values are compared with `eq'.
830 If the property is constant all the way to the start of OBJECT, return the
831 first valid position in OBJECT.
832 If the optional fourth argument LIMIT is non-nil, don't search
833 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
834 (position
, prop
, object
, limit
)
835 Lisp_Object prop
, position
, object
, limit
;
837 if (STRINGP (object
))
839 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
843 position
= make_number (SCHARS (object
));
850 int count
= SPECPDL_INDEX ();
853 CHECK_BUFFER (object
);
855 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
857 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
858 Fset_buffer (object
);
862 XSETFASTINT (limit
, BUF_BEGV (current_buffer
));
864 CHECK_NUMBER_COERCE_MARKER (limit
);
866 if (XFASTINT (position
) <= XFASTINT (limit
))
870 Lisp_Object initial_value
=
871 Fget_char_property (make_number (XFASTINT (position
) - 1),
876 position
= Fprevious_char_property_change (position
, limit
);
878 if (XFASTINT (position
) <= XFASTINT (limit
))
886 Fget_char_property (make_number (XFASTINT (position
) - 1),
889 if (!EQ (value
, initial_value
))
895 unbind_to (count
, Qnil
);
901 DEFUN ("next-property-change", Fnext_property_change
,
902 Snext_property_change
, 1, 3, 0,
903 doc
: /* Return the position of next property change.
904 Scans characters forward from POSITION in OBJECT till it finds
905 a change in some text property, then returns the position of the change.
906 The optional second argument OBJECT is the string or buffer to scan.
907 Return nil if the property is constant all the way to the end of OBJECT.
908 If the value is non-nil, it is a position greater than POSITION, never equal.
910 If the optional third argument LIMIT is non-nil, don't search
911 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
912 (position
, object
, limit
)
913 Lisp_Object position
, object
, limit
;
915 register INTERVAL i
, next
;
918 XSETBUFFER (object
, current_buffer
);
920 if (!NILP (limit
) && !EQ (limit
, Qt
))
921 CHECK_NUMBER_COERCE_MARKER (limit
);
923 i
= validate_interval_range (object
, &position
, &position
, soft
);
925 /* If LIMIT is t, return start of next interval--don't
926 bother checking further intervals. */
929 if (NULL_INTERVAL_P (i
))
932 next
= next_interval (i
);
934 if (NULL_INTERVAL_P (next
))
935 XSETFASTINT (position
, (STRINGP (object
)
937 : BUF_ZV (XBUFFER (object
))));
939 XSETFASTINT (position
, next
->position
);
943 if (NULL_INTERVAL_P (i
))
946 next
= next_interval (i
);
948 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
949 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
950 next
= next_interval (next
);
952 if (NULL_INTERVAL_P (next
))
955 XSETFASTINT (limit
, (STRINGP (object
)
957 : BUF_ZV (XBUFFER (object
))));
958 if (!(next
->position
< XFASTINT (limit
)))
961 XSETFASTINT (position
, next
->position
);
965 /* Return 1 if there's a change in some property between BEG and END. */
968 property_change_between_p (beg
, end
)
971 register INTERVAL i
, next
;
972 Lisp_Object object
, pos
;
974 XSETBUFFER (object
, current_buffer
);
975 XSETFASTINT (pos
, beg
);
977 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
978 if (NULL_INTERVAL_P (i
))
981 next
= next_interval (i
);
982 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
984 next
= next_interval (next
);
985 if (NULL_INTERVAL_P (next
))
987 if (next
->position
>= end
)
991 if (NULL_INTERVAL_P (next
))
997 DEFUN ("next-single-property-change", Fnext_single_property_change
,
998 Snext_single_property_change
, 2, 4, 0,
999 doc
: /* Return the position of next property change for a specific property.
1000 Scans characters forward from POSITION till it finds
1001 a change in the PROP property, then returns the position of the change.
1002 The optional third argument OBJECT is the string or buffer to scan.
1003 The property values are compared with `eq'.
1004 Return nil if the property is constant all the way to the end of OBJECT.
1005 If the value is non-nil, it is a position greater than POSITION, never equal.
1007 If the optional fourth argument LIMIT is non-nil, don't search
1008 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1009 (position
, prop
, object
, limit
)
1010 Lisp_Object position
, prop
, object
, limit
;
1012 register INTERVAL i
, next
;
1013 register Lisp_Object here_val
;
1016 XSETBUFFER (object
, current_buffer
);
1019 CHECK_NUMBER_COERCE_MARKER (limit
);
1021 i
= validate_interval_range (object
, &position
, &position
, soft
);
1022 if (NULL_INTERVAL_P (i
))
1025 here_val
= textget (i
->plist
, prop
);
1026 next
= next_interval (i
);
1027 while (! NULL_INTERVAL_P (next
)
1028 && EQ (here_val
, textget (next
->plist
, prop
))
1029 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1030 next
= next_interval (next
);
1032 if (NULL_INTERVAL_P (next
))
1035 XSETFASTINT (limit
, (STRINGP (object
)
1037 : BUF_ZV (XBUFFER (object
))));
1038 if (!(next
->position
< XFASTINT (limit
)))
1041 return make_number (next
->position
);
1044 DEFUN ("previous-property-change", Fprevious_property_change
,
1045 Sprevious_property_change
, 1, 3, 0,
1046 doc
: /* Return the position of previous property change.
1047 Scans characters backwards from POSITION in OBJECT till it finds
1048 a change in some text property, then returns the position of the change.
1049 The optional second argument OBJECT is the string or buffer to scan.
1050 Return nil if the property is constant all the way to the start of OBJECT.
1051 If the value is non-nil, it is a position less than POSITION, never equal.
1053 If the optional third argument LIMIT is non-nil, don't search
1054 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1055 (position
, object
, limit
)
1056 Lisp_Object position
, object
, limit
;
1058 register INTERVAL i
, previous
;
1061 XSETBUFFER (object
, current_buffer
);
1064 CHECK_NUMBER_COERCE_MARKER (limit
);
1066 i
= validate_interval_range (object
, &position
, &position
, soft
);
1067 if (NULL_INTERVAL_P (i
))
1070 /* Start with the interval containing the char before point. */
1071 if (i
->position
== XFASTINT (position
))
1072 i
= previous_interval (i
);
1074 previous
= previous_interval (i
);
1075 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1077 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1078 previous
= previous_interval (previous
);
1079 if (NULL_INTERVAL_P (previous
))
1082 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1083 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1086 return make_number (previous
->position
+ LENGTH (previous
));
1089 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1090 Sprevious_single_property_change
, 2, 4, 0,
1091 doc
: /* Return the position of previous property change for a specific property.
1092 Scans characters backward from POSITION till it finds
1093 a change in the PROP property, then returns the position of the change.
1094 The optional third argument OBJECT is the string or buffer to scan.
1095 The property values are compared with `eq'.
1096 Return nil if the property is constant all the way to the start of OBJECT.
1097 If the value is non-nil, it is a position less than POSITION, never equal.
1099 If the optional fourth argument LIMIT is non-nil, don't search
1100 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1101 (position
, prop
, object
, limit
)
1102 Lisp_Object position
, prop
, object
, limit
;
1104 register INTERVAL i
, previous
;
1105 register Lisp_Object here_val
;
1108 XSETBUFFER (object
, current_buffer
);
1111 CHECK_NUMBER_COERCE_MARKER (limit
);
1113 i
= validate_interval_range (object
, &position
, &position
, soft
);
1115 /* Start with the interval containing the char before point. */
1116 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1117 i
= previous_interval (i
);
1119 if (NULL_INTERVAL_P (i
))
1122 here_val
= textget (i
->plist
, prop
);
1123 previous
= previous_interval (i
);
1124 while (!NULL_INTERVAL_P (previous
)
1125 && EQ (here_val
, textget (previous
->plist
, prop
))
1127 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1128 previous
= previous_interval (previous
);
1129 if (NULL_INTERVAL_P (previous
))
1132 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1133 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1136 return make_number (previous
->position
+ LENGTH (previous
));
1139 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1141 DEFUN ("add-text-properties", Fadd_text_properties
,
1142 Sadd_text_properties
, 3, 4, 0,
1143 doc
: /* Add properties to the text from START to END.
1144 The third argument PROPERTIES is a property list
1145 specifying the property values to add.
1146 The optional fourth argument, OBJECT,
1147 is the string or buffer containing the text.
1148 Return t if any property value actually changed, nil otherwise. */)
1149 (start
, end
, properties
, object
)
1150 Lisp_Object start
, end
, properties
, object
;
1152 register INTERVAL i
, unchanged
;
1153 register int s
, len
, modified
= 0;
1154 struct gcpro gcpro1
;
1156 properties
= validate_plist (properties
);
1157 if (NILP (properties
))
1161 XSETBUFFER (object
, current_buffer
);
1163 i
= validate_interval_range (object
, &start
, &end
, hard
);
1164 if (NULL_INTERVAL_P (i
))
1168 len
= XINT (end
) - s
;
1170 /* No need to protect OBJECT, because we GC only if it's a buffer,
1171 and live buffers are always protected. */
1172 GCPRO1 (properties
);
1174 /* If we're not starting on an interval boundary, we have to
1175 split this interval. */
1176 if (i
->position
!= s
)
1178 /* If this interval already has the properties, we can
1180 if (interval_has_all_properties (properties
, i
))
1182 int got
= (LENGTH (i
) - (s
- i
->position
));
1184 RETURN_UNGCPRO (Qnil
);
1186 i
= next_interval (i
);
1191 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1192 copy_properties (unchanged
, i
);
1196 if (BUFFERP (object
))
1197 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1199 /* We are at the beginning of interval I, with LEN chars to scan. */
1205 if (LENGTH (i
) >= len
)
1207 /* We can UNGCPRO safely here, because there will be just
1208 one more chance to gc, in the next call to add_properties,
1209 and after that we will not need PROPERTIES or OBJECT again. */
1212 if (interval_has_all_properties (properties
, i
))
1214 if (BUFFERP (object
))
1215 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1216 XINT (end
) - XINT (start
));
1218 return modified
? Qt
: Qnil
;
1221 if (LENGTH (i
) == len
)
1223 add_properties (properties
, i
, object
);
1224 if (BUFFERP (object
))
1225 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1226 XINT (end
) - XINT (start
));
1230 /* i doesn't have the properties, and goes past the change limit */
1232 i
= split_interval_left (unchanged
, len
);
1233 copy_properties (unchanged
, i
);
1234 add_properties (properties
, i
, object
);
1235 if (BUFFERP (object
))
1236 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1237 XINT (end
) - XINT (start
));
1242 modified
+= add_properties (properties
, i
, object
);
1243 i
= next_interval (i
);
1247 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1249 DEFUN ("put-text-property", Fput_text_property
,
1250 Sput_text_property
, 4, 5, 0,
1251 doc
: /* Set one property of the text from START to END.
1252 The third and fourth arguments PROPERTY and VALUE
1253 specify the property to add.
1254 The optional fifth argument, OBJECT,
1255 is the string or buffer containing the text. */)
1256 (start
, end
, property
, value
, object
)
1257 Lisp_Object start
, end
, property
, value
, object
;
1259 Fadd_text_properties (start
, end
,
1260 Fcons (property
, Fcons (value
, Qnil
)),
1265 DEFUN ("set-text-properties", Fset_text_properties
,
1266 Sset_text_properties
, 3, 4, 0,
1267 doc
: /* Completely replace properties of text from START to END.
1268 The third argument PROPERTIES is the new property list.
1269 The optional fourth argument, OBJECT,
1270 is the string or buffer containing the text.
1271 If OBJECT is omitted or nil, it defaults to the current buffer.
1272 If PROPERTIES is nil, the effect is to remove all properties from
1273 the designated part of OBJECT. */)
1274 (start
, end
, properties
, object
)
1275 Lisp_Object start
, end
, properties
, object
;
1277 return set_text_properties (start
, end
, properties
, object
, Qt
);
1281 /* Replace properties of text from START to END with new list of
1282 properties PROPERTIES. OBJECT is the buffer or string containing
1283 the text. OBJECT nil means use the current buffer.
1284 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1285 is non-nil if properties were replaced; it is nil if there weren't
1286 any properties to replace. */
1289 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1290 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1292 register INTERVAL i
;
1293 Lisp_Object ostart
, oend
;
1298 properties
= validate_plist (properties
);
1301 XSETBUFFER (object
, current_buffer
);
1303 /* If we want no properties for a whole string,
1304 get rid of its intervals. */
1305 if (NILP (properties
) && STRINGP (object
)
1306 && XFASTINT (start
) == 0
1307 && XFASTINT (end
) == SCHARS (object
))
1309 if (! STRING_INTERVALS (object
))
1312 STRING_SET_INTERVALS (object
, NULL_INTERVAL
);
1316 i
= validate_interval_range (object
, &start
, &end
, soft
);
1318 if (NULL_INTERVAL_P (i
))
1320 /* If buffer has no properties, and we want none, return now. */
1321 if (NILP (properties
))
1324 /* Restore the original START and END values
1325 because validate_interval_range increments them for strings. */
1329 i
= validate_interval_range (object
, &start
, &end
, hard
);
1330 /* This can return if start == end. */
1331 if (NULL_INTERVAL_P (i
))
1335 if (BUFFERP (object
))
1336 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1338 set_text_properties_1 (start
, end
, properties
, object
, i
);
1340 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1341 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1342 XINT (end
) - XINT (start
));
1346 /* Replace properties of text from START to END with new list of
1347 properties PROPERTIES. BUFFER is the buffer containing
1348 the text. This does not obey any hooks.
1349 You can provide the interval that START is located in as I,
1350 or pass NULL for I and this function will find it.
1351 START and END can be in any order. */
1354 set_text_properties_1 (start
, end
, properties
, buffer
, i
)
1355 Lisp_Object start
, end
, properties
, buffer
;
1358 register INTERVAL prev_changed
= NULL_INTERVAL
;
1359 register int s
, len
;
1363 len
= XINT (end
) - s
;
1373 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1375 if (i
->position
!= s
)
1378 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1380 if (LENGTH (i
) > len
)
1382 copy_properties (unchanged
, i
);
1383 i
= split_interval_left (i
, len
);
1384 set_properties (properties
, i
, buffer
);
1388 set_properties (properties
, i
, buffer
);
1390 if (LENGTH (i
) == len
)
1395 i
= next_interval (i
);
1398 /* We are starting at the beginning of an interval, I */
1404 if (LENGTH (i
) >= len
)
1406 if (LENGTH (i
) > len
)
1407 i
= split_interval_left (i
, len
);
1409 /* We have to call set_properties even if we are going to
1410 merge the intervals, so as to make the undo records
1411 and cause redisplay to happen. */
1412 set_properties (properties
, i
, buffer
);
1413 if (!NULL_INTERVAL_P (prev_changed
))
1414 merge_interval_left (i
);
1420 /* We have to call set_properties even if we are going to
1421 merge the intervals, so as to make the undo records
1422 and cause redisplay to happen. */
1423 set_properties (properties
, i
, buffer
);
1424 if (NULL_INTERVAL_P (prev_changed
))
1427 prev_changed
= i
= merge_interval_left (i
);
1429 i
= next_interval (i
);
1433 DEFUN ("remove-text-properties", Fremove_text_properties
,
1434 Sremove_text_properties
, 3, 4, 0,
1435 doc
: /* Remove some properties from text from START to END.
1436 The third argument PROPERTIES is a property list
1437 whose property names specify the properties to remove.
1438 \(The values stored in PROPERTIES are ignored.)
1439 The optional fourth argument, OBJECT,
1440 is the string or buffer containing the text.
1441 Return t if any property was actually removed, nil otherwise. */)
1442 (start
, end
, properties
, object
)
1443 Lisp_Object start
, end
, properties
, object
;
1445 register INTERVAL i
, unchanged
;
1446 register int s
, len
, modified
= 0;
1449 XSETBUFFER (object
, current_buffer
);
1451 i
= validate_interval_range (object
, &start
, &end
, soft
);
1452 if (NULL_INTERVAL_P (i
))
1456 len
= XINT (end
) - s
;
1458 if (i
->position
!= s
)
1460 /* No properties on this first interval -- return if
1461 it covers the entire region. */
1462 if (! interval_has_some_properties (properties
, i
))
1464 int got
= (LENGTH (i
) - (s
- i
->position
));
1468 i
= next_interval (i
);
1470 /* Split away the beginning of this interval; what we don't
1475 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1476 copy_properties (unchanged
, i
);
1480 if (BUFFERP (object
))
1481 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1483 /* We are at the beginning of an interval, with len to scan */
1489 if (LENGTH (i
) >= len
)
1491 if (! interval_has_some_properties (properties
, i
))
1492 return modified
? Qt
: Qnil
;
1494 if (LENGTH (i
) == len
)
1496 remove_properties (properties
, Qnil
, i
, object
);
1497 if (BUFFERP (object
))
1498 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1499 XINT (end
) - XINT (start
));
1503 /* i has the properties, and goes past the change limit */
1505 i
= split_interval_left (i
, len
);
1506 copy_properties (unchanged
, i
);
1507 remove_properties (properties
, Qnil
, i
, object
);
1508 if (BUFFERP (object
))
1509 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1510 XINT (end
) - XINT (start
));
1515 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1516 i
= next_interval (i
);
1520 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1521 Sremove_list_of_text_properties
, 3, 4, 0,
1522 doc
: /* Remove some properties from text from START to END.
1523 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1524 The optional fourth argument, OBJECT,
1525 is the string or buffer containing the text, defaulting to the current buffer.
1526 Return t if any property was actually removed, nil otherwise. */)
1527 (start
, end
, list_of_properties
, object
)
1528 Lisp_Object start
, end
, list_of_properties
, object
;
1530 register INTERVAL i
, unchanged
;
1531 register int s
, len
, modified
= 0;
1532 Lisp_Object properties
;
1533 properties
= list_of_properties
;
1536 XSETBUFFER (object
, current_buffer
);
1538 i
= validate_interval_range (object
, &start
, &end
, soft
);
1539 if (NULL_INTERVAL_P (i
))
1543 len
= XINT (end
) - s
;
1545 if (i
->position
!= s
)
1547 /* No properties on this first interval -- return if
1548 it covers the entire region. */
1549 if (! interval_has_some_properties_list (properties
, i
))
1551 int got
= (LENGTH (i
) - (s
- i
->position
));
1555 i
= next_interval (i
);
1557 /* Split away the beginning of this interval; what we don't
1562 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1563 copy_properties (unchanged
, i
);
1567 if (BUFFERP (object
))
1568 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1570 /* We are at the beginning of an interval, with len to scan */
1576 if (LENGTH (i
) >= len
)
1578 if (! interval_has_some_properties_list (properties
, i
))
1579 return modified
? Qt
: Qnil
;
1581 if (LENGTH (i
) == len
)
1583 remove_properties (Qnil
, properties
, i
, object
);
1584 if (BUFFERP (object
))
1585 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1586 XINT (end
) - XINT (start
));
1590 /* i has the properties, and goes past the change limit */
1592 i
= split_interval_left (i
, len
);
1593 copy_properties (unchanged
, i
);
1594 remove_properties (Qnil
, properties
, i
, object
);
1595 if (BUFFERP (object
))
1596 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1597 XINT (end
) - XINT (start
));
1602 modified
+= remove_properties (Qnil
, properties
, i
, object
);
1603 i
= next_interval (i
);
1607 DEFUN ("text-property-any", Ftext_property_any
,
1608 Stext_property_any
, 4, 5, 0,
1609 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1610 If so, return the position of the first character whose property PROPERTY
1611 is `eq' to VALUE. Otherwise return nil.
1612 The optional fifth argument, OBJECT, is the string or buffer
1613 containing the text. */)
1614 (start
, end
, property
, value
, object
)
1615 Lisp_Object start
, end
, property
, value
, object
;
1617 register INTERVAL i
;
1618 register int e
, pos
;
1621 XSETBUFFER (object
, current_buffer
);
1622 i
= validate_interval_range (object
, &start
, &end
, soft
);
1623 if (NULL_INTERVAL_P (i
))
1624 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1627 while (! NULL_INTERVAL_P (i
))
1629 if (i
->position
>= e
)
1631 if (EQ (textget (i
->plist
, property
), value
))
1634 if (pos
< XINT (start
))
1636 return make_number (pos
);
1638 i
= next_interval (i
);
1643 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1644 Stext_property_not_all
, 4, 5, 0,
1645 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1646 If so, return the position of the first character whose property PROPERTY
1647 is not `eq' to VALUE. Otherwise, return nil.
1648 The optional fifth argument, OBJECT, is the string or buffer
1649 containing the text. */)
1650 (start
, end
, property
, value
, object
)
1651 Lisp_Object start
, end
, property
, value
, object
;
1653 register INTERVAL i
;
1657 XSETBUFFER (object
, current_buffer
);
1658 i
= validate_interval_range (object
, &start
, &end
, soft
);
1659 if (NULL_INTERVAL_P (i
))
1660 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1664 while (! NULL_INTERVAL_P (i
))
1666 if (i
->position
>= e
)
1668 if (! EQ (textget (i
->plist
, property
), value
))
1670 if (i
->position
> s
)
1672 return make_number (s
);
1674 i
= next_interval (i
);
1680 /* Return the direction from which the text-property PROP would be
1681 inherited by any new text inserted at POS: 1 if it would be
1682 inherited from the char after POS, -1 if it would be inherited from
1683 the char before POS, and 0 if from neither. */
1686 text_property_stickiness (prop
, pos
)
1690 Lisp_Object prev_pos
, front_sticky
;
1691 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1693 if (XINT (pos
) > BEGV
)
1694 /* Consider previous character. */
1696 Lisp_Object rear_non_sticky
;
1698 prev_pos
= make_number (XINT (pos
) - 1);
1699 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, Qnil
);
1701 if (!NILP (CONSP (rear_non_sticky
)
1702 ? Fmemq (prop
, rear_non_sticky
)
1704 /* PROP is rear-non-sticky. */
1708 /* Consider following character. */
1709 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, Qnil
);
1711 if (EQ (front_sticky
, Qt
)
1712 || (CONSP (front_sticky
)
1713 && !NILP (Fmemq (prop
, front_sticky
))))
1714 /* PROP is inherited from after. */
1715 is_front_sticky
= 1;
1717 /* Simple cases, where the properties are consistent. */
1718 if (is_rear_sticky
&& !is_front_sticky
)
1720 else if (!is_rear_sticky
&& is_front_sticky
)
1722 else if (!is_rear_sticky
&& !is_front_sticky
)
1725 /* The stickiness properties are inconsistent, so we have to
1726 disambiguate. Basically, rear-sticky wins, _except_ if the
1727 property that would be inherited has a value of nil, in which case
1728 front-sticky wins. */
1729 if (XINT (pos
) == BEGV
|| NILP (Fget_text_property (prev_pos
, prop
, Qnil
)))
1736 /* I don't think this is the right interface to export; how often do you
1737 want to do something like this, other than when you're copying objects
1740 I think it would be better to have a pair of functions, one which
1741 returns the text properties of a region as a list of ranges and
1742 plists, and another which applies such a list to another object. */
1744 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1745 SRC and DEST may each refer to strings or buffers.
1746 Optional sixth argument PROP causes only that property to be copied.
1747 Properties are copied to DEST as if by `add-text-properties'.
1748 Return t if any property value actually changed, nil otherwise. */
1750 /* Note this can GC when DEST is a buffer. */
1753 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1754 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1760 int s
, e
, e2
, p
, len
, modified
= 0;
1761 struct gcpro gcpro1
, gcpro2
;
1763 i
= validate_interval_range (src
, &start
, &end
, soft
);
1764 if (NULL_INTERVAL_P (i
))
1767 CHECK_NUMBER_COERCE_MARKER (pos
);
1769 Lisp_Object dest_start
, dest_end
;
1772 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1773 /* Apply this to a copy of pos; it will try to increment its arguments,
1774 which we don't want. */
1775 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1786 e2
= i
->position
+ LENGTH (i
);
1793 while (! NILP (plist
))
1795 if (EQ (Fcar (plist
), prop
))
1797 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1800 plist
= Fcdr (Fcdr (plist
));
1804 /* Must defer modifications to the interval tree in case src
1805 and dest refer to the same string or buffer. */
1806 stuff
= Fcons (Fcons (make_number (p
),
1807 Fcons (make_number (p
+ len
),
1808 Fcons (plist
, Qnil
))),
1812 i
= next_interval (i
);
1813 if (NULL_INTERVAL_P (i
))
1820 GCPRO2 (stuff
, dest
);
1822 while (! NILP (stuff
))
1825 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1826 Fcar (Fcdr (Fcdr (res
))), dest
);
1829 stuff
= Fcdr (stuff
);
1834 return modified
? Qt
: Qnil
;
1838 /* Return a list representing the text properties of OBJECT between
1839 START and END. if PROP is non-nil, report only on that property.
1840 Each result list element has the form (S E PLIST), where S and E
1841 are positions in OBJECT and PLIST is a property list containing the
1842 text properties of OBJECT between S and E. Value is nil if OBJECT
1843 doesn't contain text properties between START and END. */
1846 text_property_list (object
, start
, end
, prop
)
1847 Lisp_Object object
, start
, end
, prop
;
1854 i
= validate_interval_range (object
, &start
, &end
, soft
);
1855 if (!NULL_INTERVAL_P (i
))
1857 int s
= XINT (start
);
1862 int interval_end
, len
;
1865 interval_end
= i
->position
+ LENGTH (i
);
1866 if (interval_end
> e
)
1868 len
= interval_end
- s
;
1873 for (; !NILP (plist
); plist
= Fcdr (Fcdr (plist
)))
1874 if (EQ (Fcar (plist
), prop
))
1876 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1881 result
= Fcons (Fcons (make_number (s
),
1882 Fcons (make_number (s
+ len
),
1883 Fcons (plist
, Qnil
))),
1886 i
= next_interval (i
);
1887 if (NULL_INTERVAL_P (i
))
1897 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1898 (START END PLIST), where START and END are positions and PLIST is a
1899 property list containing the text properties to add. Adjust START
1900 and END positions by DELTA before adding properties. Value is
1901 non-zero if OBJECT was modified. */
1904 add_text_properties_from_list (object
, list
, delta
)
1905 Lisp_Object object
, list
, delta
;
1907 struct gcpro gcpro1
, gcpro2
;
1910 GCPRO2 (list
, object
);
1912 for (; CONSP (list
); list
= XCDR (list
))
1914 Lisp_Object item
, start
, end
, plist
, tem
;
1917 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1918 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1919 plist
= XCAR (XCDR (XCDR (item
)));
1921 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1932 /* Modify end-points of ranges in LIST destructively. LIST is a list
1933 as returned from text_property_list. Change end-points equal to
1934 OLD_END to NEW_END. */
1937 extend_property_ranges (list
, old_end
, new_end
)
1938 Lisp_Object list
, old_end
, new_end
;
1940 for (; CONSP (list
); list
= XCDR (list
))
1942 Lisp_Object item
, end
;
1945 end
= XCAR (XCDR (item
));
1947 if (EQ (end
, old_end
))
1948 XSETCAR (XCDR (item
), new_end
);
1954 /* Call the modification hook functions in LIST, each with START and END. */
1957 call_mod_hooks (list
, start
, end
)
1958 Lisp_Object list
, start
, end
;
1960 struct gcpro gcpro1
;
1962 while (!NILP (list
))
1964 call2 (Fcar (list
), start
, end
);
1970 /* Check for read-only intervals between character positions START ... END,
1971 in BUF, and signal an error if we find one.
1973 Then check for any modification hooks in the range.
1974 Create a list of all these hooks in lexicographic order,
1975 eliminating consecutive extra copies of the same hook. Then call
1976 those hooks in order, with START and END - 1 as arguments. */
1979 verify_interval_modification (buf
, start
, end
)
1983 register INTERVAL intervals
= BUF_INTERVALS (buf
);
1984 register INTERVAL i
;
1986 register Lisp_Object prev_mod_hooks
;
1987 Lisp_Object mod_hooks
;
1988 struct gcpro gcpro1
;
1991 prev_mod_hooks
= Qnil
;
1994 interval_insert_behind_hooks
= Qnil
;
1995 interval_insert_in_front_hooks
= Qnil
;
1997 if (NULL_INTERVAL_P (intervals
))
2007 /* For an insert operation, check the two chars around the position. */
2010 INTERVAL prev
= NULL
;
2011 Lisp_Object before
, after
;
2013 /* Set I to the interval containing the char after START,
2014 and PREV to the interval containing the char before START.
2015 Either one may be null. They may be equal. */
2016 i
= find_interval (intervals
, start
);
2018 if (start
== BUF_BEGV (buf
))
2020 else if (i
->position
== start
)
2021 prev
= previous_interval (i
);
2022 else if (i
->position
< start
)
2024 if (start
== BUF_ZV (buf
))
2027 /* If Vinhibit_read_only is set and is not a list, we can
2028 skip the read_only checks. */
2029 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2031 /* If I and PREV differ we need to check for the read-only
2032 property together with its stickiness. If either I or
2033 PREV are 0, this check is all we need.
2034 We have to take special care, since read-only may be
2035 indirectly defined via the category property. */
2038 if (! NULL_INTERVAL_P (i
))
2040 after
= textget (i
->plist
, Qread_only
);
2042 /* If interval I is read-only and read-only is
2043 front-sticky, inhibit insertion.
2044 Check for read-only as well as category. */
2046 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2050 tem
= textget (i
->plist
, Qfront_sticky
);
2051 if (TMEM (Qread_only
, tem
)
2052 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2053 && TMEM (Qcategory
, tem
)))
2054 text_read_only (after
);
2058 if (! NULL_INTERVAL_P (prev
))
2060 before
= textget (prev
->plist
, Qread_only
);
2062 /* If interval PREV is read-only and read-only isn't
2063 rear-nonsticky, inhibit insertion.
2064 Check for read-only as well as category. */
2066 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2070 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2071 if (! TMEM (Qread_only
, tem
)
2072 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2073 || ! TMEM (Qcategory
, tem
)))
2074 text_read_only (before
);
2078 else if (! NULL_INTERVAL_P (i
))
2080 after
= textget (i
->plist
, Qread_only
);
2082 /* If interval I is read-only and read-only is
2083 front-sticky, inhibit insertion.
2084 Check for read-only as well as category. */
2085 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2089 tem
= textget (i
->plist
, Qfront_sticky
);
2090 if (TMEM (Qread_only
, tem
)
2091 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2092 && TMEM (Qcategory
, tem
)))
2093 text_read_only (after
);
2095 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2096 if (! TMEM (Qread_only
, tem
)
2097 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2098 || ! TMEM (Qcategory
, tem
)))
2099 text_read_only (after
);
2104 /* Run both insert hooks (just once if they're the same). */
2105 if (!NULL_INTERVAL_P (prev
))
2106 interval_insert_behind_hooks
2107 = textget (prev
->plist
, Qinsert_behind_hooks
);
2108 if (!NULL_INTERVAL_P (i
))
2109 interval_insert_in_front_hooks
2110 = textget (i
->plist
, Qinsert_in_front_hooks
);
2114 /* Loop over intervals on or next to START...END,
2115 collecting their hooks. */
2117 i
= find_interval (intervals
, start
);
2120 if (! INTERVAL_WRITABLE_P (i
))
2121 text_read_only (textget (i
->plist
, Qread_only
));
2123 if (!inhibit_modification_hooks
)
2125 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2126 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2128 hooks
= Fcons (mod_hooks
, hooks
);
2129 prev_mod_hooks
= mod_hooks
;
2133 i
= next_interval (i
);
2135 /* Keep going thru the interval containing the char before END. */
2136 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2138 if (!inhibit_modification_hooks
)
2141 hooks
= Fnreverse (hooks
);
2142 while (! EQ (hooks
, Qnil
))
2144 call_mod_hooks (Fcar (hooks
), make_number (start
),
2146 hooks
= Fcdr (hooks
);
2153 /* Run the interval hooks for an insertion on character range START ... END.
2154 verify_interval_modification chose which hooks to run;
2155 this function is called after the insertion happens
2156 so it can indicate the range of inserted text. */
2159 report_interval_modification (start
, end
)
2160 Lisp_Object start
, end
;
2162 if (! NILP (interval_insert_behind_hooks
))
2163 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2164 if (! NILP (interval_insert_in_front_hooks
)
2165 && ! EQ (interval_insert_in_front_hooks
,
2166 interval_insert_behind_hooks
))
2167 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2173 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
2174 doc
: /* Property-list used as default values.
2175 The value of a property in this list is seen as the value for every
2176 character that does not have its own value for that property. */);
2177 Vdefault_text_properties
= Qnil
;
2179 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist
,
2180 doc
: /* Alist of alternative properties for properties without a value.
2181 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2182 If a piece of text has no direct value for a particular property, then
2183 this alist is consulted. If that property appears in the alist, then
2184 the first non-nil value from the associated alternative properties is
2186 Vchar_property_alias_alist
= Qnil
;
2188 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
2189 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2190 This also inhibits the use of the `intangible' text property. */);
2191 Vinhibit_point_motion_hooks
= Qnil
;
2193 DEFVAR_LISP ("text-property-default-nonsticky",
2194 &Vtext_property_default_nonsticky
,
2195 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2196 Each element has the form (PROPERTY . NONSTICKINESS).
2198 If a character in a buffer has PROPERTY, new text inserted adjacent to
2199 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2200 inherits it if NONSTICKINESS is nil. The front-sticky and
2201 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2202 Vtext_property_default_nonsticky
= Qnil
;
2204 staticpro (&interval_insert_behind_hooks
);
2205 staticpro (&interval_insert_in_front_hooks
);
2206 interval_insert_behind_hooks
= Qnil
;
2207 interval_insert_in_front_hooks
= Qnil
;
2210 /* Common attributes one might give text */
2212 staticpro (&Qforeground
);
2213 Qforeground
= intern ("foreground");
2214 staticpro (&Qbackground
);
2215 Qbackground
= intern ("background");
2217 Qfont
= intern ("font");
2218 staticpro (&Qstipple
);
2219 Qstipple
= intern ("stipple");
2220 staticpro (&Qunderline
);
2221 Qunderline
= intern ("underline");
2222 staticpro (&Qread_only
);
2223 Qread_only
= intern ("read-only");
2224 staticpro (&Qinvisible
);
2225 Qinvisible
= intern ("invisible");
2226 staticpro (&Qintangible
);
2227 Qintangible
= intern ("intangible");
2228 staticpro (&Qcategory
);
2229 Qcategory
= intern ("category");
2230 staticpro (&Qlocal_map
);
2231 Qlocal_map
= intern ("local-map");
2232 staticpro (&Qfront_sticky
);
2233 Qfront_sticky
= intern ("front-sticky");
2234 staticpro (&Qrear_nonsticky
);
2235 Qrear_nonsticky
= intern ("rear-nonsticky");
2236 staticpro (&Qmouse_face
);
2237 Qmouse_face
= intern ("mouse-face");
2239 /* Properties that text might use to specify certain actions */
2241 staticpro (&Qmouse_left
);
2242 Qmouse_left
= intern ("mouse-left");
2243 staticpro (&Qmouse_entered
);
2244 Qmouse_entered
= intern ("mouse-entered");
2245 staticpro (&Qpoint_left
);
2246 Qpoint_left
= intern ("point-left");
2247 staticpro (&Qpoint_entered
);
2248 Qpoint_entered
= intern ("point-entered");
2250 defsubr (&Stext_properties_at
);
2251 defsubr (&Sget_text_property
);
2252 defsubr (&Sget_char_property
);
2253 defsubr (&Snext_char_property_change
);
2254 defsubr (&Sprevious_char_property_change
);
2255 defsubr (&Snext_single_char_property_change
);
2256 defsubr (&Sprevious_single_char_property_change
);
2257 defsubr (&Snext_property_change
);
2258 defsubr (&Snext_single_property_change
);
2259 defsubr (&Sprevious_property_change
);
2260 defsubr (&Sprevious_single_property_change
);
2261 defsubr (&Sadd_text_properties
);
2262 defsubr (&Sput_text_property
);
2263 defsubr (&Sset_text_properties
);
2264 defsubr (&Sremove_text_properties
);
2265 defsubr (&Sremove_list_of_text_properties
);
2266 defsubr (&Stext_property_any
);
2267 defsubr (&Stext_property_not_all
);
2268 /* defsubr (&Serase_text_properties); */
2269 /* defsubr (&Scopy_text_properties); */