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 Vtext_property_default_nonsticky
;
75 /* verify_interval_modification saves insertion hooks here
76 to be run later by report_interval_modification. */
77 Lisp_Object interval_insert_behind_hooks
;
78 Lisp_Object interval_insert_in_front_hooks
;
81 /* Signal a `text-read-only' error. This function makes it easier
82 to capture that error in GDB by putting a breakpoint on it. */
87 Fsignal (Qtext_read_only
, Qnil
);
92 /* Extract the interval at the position pointed to by BEGIN from
93 OBJECT, a string or buffer. Additionally, check that the positions
94 pointed to by BEGIN and END are within the bounds of OBJECT, and
95 reverse them if *BEGIN is greater than *END. The objects pointed
96 to by BEGIN and END may be integers or markers; if the latter, they
97 are coerced to integers.
99 When OBJECT is a string, we increment *BEGIN and *END
100 to make them origin-one.
102 Note that buffer points don't correspond to interval indices.
103 For example, point-max is 1 greater than the index of the last
104 character. This difference is handled in the caller, which uses
105 the validated points to determine a length, and operates on that.
106 Exceptions are Ftext_properties_at, Fnext_property_change, and
107 Fprevious_property_change which call this function with BEGIN == END.
108 Handle this case specially.
110 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
111 create an interval tree for OBJECT if one doesn't exist, provided
112 the object actually contains text. In the current design, if there
113 is no text, there can be no text properties. */
119 validate_interval_range (object
, begin
, end
, force
)
120 Lisp_Object object
, *begin
, *end
;
126 CHECK_STRING_OR_BUFFER (object
);
127 CHECK_NUMBER_COERCE_MARKER (*begin
);
128 CHECK_NUMBER_COERCE_MARKER (*end
);
130 /* If we are asked for a point, but from a subr which operates
131 on a range, then return nothing. */
132 if (EQ (*begin
, *end
) && begin
!= end
)
133 return NULL_INTERVAL
;
135 if (XINT (*begin
) > XINT (*end
))
143 if (BUFFERP (object
))
145 register struct buffer
*b
= XBUFFER (object
);
147 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
148 && XINT (*end
) <= BUF_ZV (b
)))
149 args_out_of_range (*begin
, *end
);
150 i
= BUF_INTERVALS (b
);
152 /* If there's no text, there are no properties. */
153 if (BUF_BEGV (b
) == BUF_ZV (b
))
154 return NULL_INTERVAL
;
156 searchpos
= XINT (*begin
);
160 register struct Lisp_String
*s
= XSTRING (object
);
162 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
163 && XINT (*end
) <= s
->size
))
164 args_out_of_range (*begin
, *end
);
165 XSETFASTINT (*begin
, XFASTINT (*begin
));
167 XSETFASTINT (*end
, XFASTINT (*end
));
171 return NULL_INTERVAL
;
173 searchpos
= XINT (*begin
);
176 if (NULL_INTERVAL_P (i
))
177 return (force
? create_root_interval (object
) : i
);
179 return find_interval (i
, searchpos
);
182 /* Validate LIST as a property list. If LIST is not a list, then
183 make one consisting of (LIST nil). Otherwise, verify that LIST
184 is even numbered and thus suitable as a plist. */
187 validate_plist (list
)
196 register Lisp_Object tail
;
197 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
203 error ("Odd length text property list");
207 return Fcons (list
, Fcons (Qnil
, Qnil
));
210 /* Return nonzero if interval I has all the properties,
211 with the same values, of list PLIST. */
214 interval_has_all_properties (plist
, i
)
218 register Lisp_Object tail1
, tail2
, sym1
;
221 /* Go through each element of PLIST. */
222 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
227 /* Go through I's plist, looking for sym1 */
228 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
229 if (EQ (sym1
, Fcar (tail2
)))
231 /* Found the same property on both lists. If the
232 values are unequal, return zero. */
233 if (! EQ (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))))
236 /* Property has same value on both lists; go to next one. */
248 /* Return nonzero if the plist of interval I has any of the
249 properties of PLIST, regardless of their values. */
252 interval_has_some_properties (plist
, i
)
256 register Lisp_Object tail1
, tail2
, sym
;
258 /* Go through each element of PLIST. */
259 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
263 /* Go through i's plist, looking for tail1 */
264 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
265 if (EQ (sym
, Fcar (tail2
)))
272 /* Return nonzero if the plist of interval I has any of the
273 property names in LIST, regardless of their values. */
276 interval_has_some_properties_list (list
, i
)
280 register Lisp_Object tail1
, tail2
, sym
;
282 /* Go through each element of LIST. */
283 for (tail1
= list
; ! NILP (tail1
); tail1
= XCDR (tail1
))
287 /* Go through i's plist, looking for tail1 */
288 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= XCDR (XCDR (tail2
)))
289 if (EQ (sym
, XCAR (tail2
)))
296 /* Changing the plists of individual intervals. */
298 /* Return the value of PROP in property-list PLIST, or Qunbound if it
301 property_value (plist
, prop
)
302 Lisp_Object plist
, prop
;
306 while (PLIST_ELT_P (plist
, value
))
307 if (EQ (XCAR (plist
), prop
))
310 plist
= XCDR (value
);
315 /* Set the properties of INTERVAL to PROPERTIES,
316 and record undo info for the previous values.
317 OBJECT is the string or buffer that INTERVAL belongs to. */
320 set_properties (properties
, interval
, object
)
321 Lisp_Object properties
, object
;
324 Lisp_Object sym
, value
;
326 if (BUFFERP (object
))
328 /* For each property in the old plist which is missing from PROPERTIES,
329 or has a different value in PROPERTIES, make an undo record. */
330 for (sym
= interval
->plist
;
331 PLIST_ELT_P (sym
, value
);
333 if (! EQ (property_value (properties
, XCAR (sym
)),
336 record_property_change (interval
->position
, LENGTH (interval
),
337 XCAR (sym
), XCAR (value
),
341 /* For each new property that has no value at all in the old plist,
342 make an undo record binding it to nil, so it will be removed. */
343 for (sym
= properties
;
344 PLIST_ELT_P (sym
, value
);
346 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
348 record_property_change (interval
->position
, LENGTH (interval
),
354 /* Store new properties. */
355 interval
->plist
= Fcopy_sequence (properties
);
358 /* Add the properties of PLIST to the interval I, or set
359 the value of I's property to the value of the property on PLIST
360 if they are different.
362 OBJECT should be the string or buffer the interval is in.
364 Return nonzero if this changes I (i.e., if any members of PLIST
365 are actually added to I's plist) */
368 add_properties (plist
, i
, object
)
373 Lisp_Object tail1
, tail2
, sym1
, val1
;
374 register int changed
= 0;
376 struct gcpro gcpro1
, gcpro2
, gcpro3
;
381 /* No need to protect OBJECT, because we can GC only in the case
382 where it is a buffer, and live buffers are always protected.
383 I and its plist are also protected, via OBJECT. */
384 GCPRO3 (tail1
, sym1
, val1
);
386 /* Go through each element of PLIST. */
387 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
390 val1
= Fcar (Fcdr (tail1
));
393 /* Go through I's plist, looking for sym1 */
394 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
395 if (EQ (sym1
, Fcar (tail2
)))
397 /* No need to gcpro, because tail2 protects this
398 and it must be a cons cell (we get an error otherwise). */
399 register Lisp_Object this_cdr
;
401 this_cdr
= Fcdr (tail2
);
402 /* Found the property. Now check its value. */
405 /* The properties have the same value on both lists.
406 Continue to the next property. */
407 if (EQ (val1
, Fcar (this_cdr
)))
410 /* Record this change in the buffer, for undo purposes. */
411 if (BUFFERP (object
))
413 record_property_change (i
->position
, LENGTH (i
),
414 sym1
, Fcar (this_cdr
), object
);
417 /* I's property has a different value -- change it */
418 Fsetcar (this_cdr
, val1
);
425 /* Record this change in the buffer, for undo purposes. */
426 if (BUFFERP (object
))
428 record_property_change (i
->position
, LENGTH (i
),
431 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
441 /* For any members of PLIST, or LIST,
442 which are properties of I, remove them from I's plist.
443 (If PLIST is non-nil, use that, otherwise use LIST.)
444 OBJECT is the string or buffer containing I. */
447 remove_properties (plist
, list
, i
, object
)
448 Lisp_Object plist
, list
;
452 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
453 register int changed
= 0;
455 /* Nonzero means tail1 is a plist, otherwise it is a list. */
458 current_plist
= i
->plist
;
461 tail1
= plist
, use_plist
= 1;
463 tail1
= list
, use_plist
= 0;
465 /* Go through each element of LIST or PLIST. */
466 while (! NILP (tail1
))
470 /* First, remove the symbol if it's at the head of the list */
471 while (! NILP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
473 if (BUFFERP (object
))
474 record_property_change (i
->position
, LENGTH (i
),
475 sym
, XCAR (XCDR (current_plist
)),
478 current_plist
= XCDR (XCDR (current_plist
));
482 /* Go through I's plist, looking for SYM. */
483 tail2
= current_plist
;
484 while (! NILP (tail2
))
486 register Lisp_Object
this;
487 this = XCDR (XCDR (tail2
));
488 if (EQ (sym
, XCAR (this)))
490 if (BUFFERP (object
))
491 record_property_change (i
->position
, LENGTH (i
),
492 sym
, XCAR (XCDR (this)), object
);
494 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
500 /* Advance thru TAIL1 one way or the other. */
501 tail1
= XCDR (tail1
);
502 if (use_plist
&& CONSP (tail1
))
503 tail1
= XCDR (tail1
);
507 i
->plist
= current_plist
;
512 /* Remove all properties from interval I. Return non-zero
513 if this changes the interval. */
527 /* Returns the interval of POSITION in OBJECT.
528 POSITION is BEG-based. */
531 interval_of (position
, object
)
539 XSETBUFFER (object
, current_buffer
);
540 else if (EQ (object
, Qt
))
541 return NULL_INTERVAL
;
543 CHECK_STRING_OR_BUFFER (object
);
545 if (BUFFERP (object
))
547 register struct buffer
*b
= XBUFFER (object
);
551 i
= BUF_INTERVALS (b
);
555 register struct Lisp_String
*s
= XSTRING (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 POSITION's property PROP, in OBJECT.
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
;
644 struct buffer
*obuf
= current_buffer
;
646 set_buffer_temp (XBUFFER (object
));
648 /* First try with room for 40 overlays. */
650 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
652 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
653 &next_overlay
, NULL
, 0);
655 /* If there are more than 40,
656 make enough space for all, and try again. */
660 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
661 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
662 &next_overlay
, NULL
, 0);
664 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
666 set_buffer_temp (obuf
);
668 /* Now check the overlays in order of decreasing priority. */
669 while (--noverlays
>= 0)
671 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
675 /* Return the overlay we got the property from. */
676 *overlay
= overlay_vec
[noverlays
];
683 /* Indicate that the return value is not from an overlay. */
686 /* Not a buffer, or no appropriate overlay, so fall through to the
688 return Fget_text_property (position
, prop
, object
);
691 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
692 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
693 Both overlay properties and text properties are checked.
694 OBJECT is optional and defaults to the current buffer.
695 If POSITION is at the end of OBJECT, the value is nil.
696 If OBJECT is a buffer, then overlay properties are considered as well as
698 If OBJECT is a window, then that window's buffer is used, but window-specific
699 overlays are considered only if they are associated with OBJECT. */)
700 (position
, prop
, object
)
701 Lisp_Object position
, object
;
702 register Lisp_Object prop
;
704 return get_char_property_and_overlay (position
, prop
, object
, 0);
707 DEFUN ("next-char-property-change", Fnext_char_property_change
,
708 Snext_char_property_change
, 1, 2, 0,
709 doc
: /* Return the position of next text property or overlay change.
710 This scans characters forward from POSITION till it finds a change in
711 some text property, or the beginning or end of an overlay, and returns
712 the position of that.
713 If none is found, the function returns (point-max).
715 If the optional third argument LIMIT is non-nil, don't search
716 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
718 Lisp_Object position
, limit
;
722 temp
= Fnext_overlay_change (position
);
725 CHECK_NUMBER (limit
);
726 if (XINT (limit
) < XINT (temp
))
729 return Fnext_property_change (position
, Qnil
, temp
);
732 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
733 Sprevious_char_property_change
, 1, 2, 0,
734 doc
: /* Return the position of previous text property or overlay change.
735 Scans characters backward from POSITION till it finds a change in some
736 text property, or the beginning or end of an overlay, and returns the
738 If none is found, the function returns (point-max).
740 If the optional third argument LIMIT is non-nil, don't search
741 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
743 Lisp_Object position
, limit
;
747 temp
= Fprevious_overlay_change (position
);
750 CHECK_NUMBER (limit
);
751 if (XINT (limit
) > XINT (temp
))
754 return Fprevious_property_change (position
, Qnil
, temp
);
758 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
759 Snext_single_char_property_change
, 2, 4, 0,
760 doc
: /* Return the position of next text property or overlay change for a specific property.
761 Scans characters forward from POSITION till it finds
762 a change in the PROP property, then returns the position of the change.
763 The optional third argument OBJECT is the string or buffer to scan.
764 The property values are compared with `eq'.
765 If the property is constant all the way to the end of OBJECT, return the
766 last valid position in OBJECT.
767 If the optional fourth argument LIMIT is non-nil, don't search
768 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
769 (position
, prop
, object
, limit
)
770 Lisp_Object prop
, position
, object
, limit
;
772 if (STRINGP (object
))
774 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
778 position
= make_number (XSTRING (object
)->size
);
785 Lisp_Object initial_value
, value
;
786 int count
= specpdl_ptr
- specpdl
;
789 CHECK_BUFFER (object
);
791 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
793 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
794 Fset_buffer (object
);
797 initial_value
= Fget_char_property (position
, prop
, object
);
800 XSETFASTINT (limit
, BUF_ZV (current_buffer
));
802 CHECK_NUMBER_COERCE_MARKER (limit
);
806 position
= Fnext_char_property_change (position
, limit
);
807 if (XFASTINT (position
) >= XFASTINT (limit
)) {
812 value
= Fget_char_property (position
, prop
, object
);
813 if (!EQ (value
, initial_value
))
817 unbind_to (count
, Qnil
);
823 DEFUN ("previous-single-char-property-change",
824 Fprevious_single_char_property_change
,
825 Sprevious_single_char_property_change
, 2, 4, 0,
826 doc
: /* Return the position of previous text property or overlay change for a specific property.
827 Scans characters backward from POSITION till it finds
828 a change in the PROP property, then returns the position of the change.
829 The optional third argument OBJECT is the string or buffer to scan.
830 The property values are compared with `eq'.
831 If the property is constant all the way to the start of OBJECT, return the
832 first valid position in OBJECT.
833 If the optional fourth argument LIMIT is non-nil, don't search
834 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
835 (position
, prop
, object
, limit
)
836 Lisp_Object prop
, position
, object
, limit
;
838 if (STRINGP (object
))
840 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
844 position
= make_number (XSTRING (object
)->size
);
851 int count
= specpdl_ptr
- specpdl
;
854 CHECK_BUFFER (object
);
856 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
858 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
859 Fset_buffer (object
);
863 XSETFASTINT (limit
, BUF_BEGV (current_buffer
));
865 CHECK_NUMBER_COERCE_MARKER (limit
);
867 if (XFASTINT (position
) <= XFASTINT (limit
))
871 Lisp_Object initial_value
=
872 Fget_char_property (make_number (XFASTINT (position
) - 1),
877 position
= Fprevious_char_property_change (position
, limit
);
879 if (XFASTINT (position
) <= XFASTINT (limit
))
887 Fget_char_property (make_number (XFASTINT (position
) - 1),
890 if (!EQ (value
, initial_value
))
896 unbind_to (count
, Qnil
);
902 DEFUN ("next-property-change", Fnext_property_change
,
903 Snext_property_change
, 1, 3, 0,
904 doc
: /* Return the position of next property change.
905 Scans characters forward from POSITION in OBJECT till it finds
906 a change in some text property, then returns the position of the change.
907 The optional second argument OBJECT is the string or buffer to scan.
908 Return nil if the property is constant all the way to the end of OBJECT.
909 If the value is non-nil, it is a position greater than POSITION, never equal.
911 If the optional third argument LIMIT is non-nil, don't search
912 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
913 (position
, object
, limit
)
914 Lisp_Object position
, object
, limit
;
916 register INTERVAL i
, next
;
919 XSETBUFFER (object
, current_buffer
);
921 if (!NILP (limit
) && !EQ (limit
, Qt
))
922 CHECK_NUMBER_COERCE_MARKER (limit
);
924 i
= validate_interval_range (object
, &position
, &position
, soft
);
926 /* If LIMIT is t, return start of next interval--don't
927 bother checking further intervals. */
930 if (NULL_INTERVAL_P (i
))
933 next
= next_interval (i
);
935 if (NULL_INTERVAL_P (next
))
936 XSETFASTINT (position
, (STRINGP (object
)
937 ? XSTRING (object
)->size
938 : BUF_ZV (XBUFFER (object
))));
940 XSETFASTINT (position
, next
->position
);
944 if (NULL_INTERVAL_P (i
))
947 next
= next_interval (i
);
949 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
950 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
951 next
= next_interval (next
);
953 if (NULL_INTERVAL_P (next
))
956 XSETFASTINT (limit
, (STRINGP (object
)
957 ? XSTRING (object
)->size
958 : BUF_ZV (XBUFFER (object
))));
959 if (!(next
->position
< XFASTINT (limit
)))
962 XSETFASTINT (position
, next
->position
);
966 /* Return 1 if there's a change in some property between BEG and END. */
969 property_change_between_p (beg
, end
)
972 register INTERVAL i
, next
;
973 Lisp_Object object
, pos
;
975 XSETBUFFER (object
, current_buffer
);
976 XSETFASTINT (pos
, beg
);
978 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
979 if (NULL_INTERVAL_P (i
))
982 next
= next_interval (i
);
983 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
985 next
= next_interval (next
);
986 if (NULL_INTERVAL_P (next
))
988 if (next
->position
>= end
)
992 if (NULL_INTERVAL_P (next
))
998 DEFUN ("next-single-property-change", Fnext_single_property_change
,
999 Snext_single_property_change
, 2, 4, 0,
1000 doc
: /* Return the position of next property change for a specific property.
1001 Scans characters forward from POSITION till it finds
1002 a change in the PROP property, then returns the position of the change.
1003 The optional third argument OBJECT is the string or buffer to scan.
1004 The property values are compared with `eq'.
1005 Return nil if the property is constant all the way to the end of OBJECT.
1006 If the value is non-nil, it is a position greater than POSITION, never equal.
1008 If the optional fourth argument LIMIT is non-nil, don't search
1009 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1010 (position
, prop
, object
, limit
)
1011 Lisp_Object position
, prop
, object
, limit
;
1013 register INTERVAL i
, next
;
1014 register Lisp_Object here_val
;
1017 XSETBUFFER (object
, current_buffer
);
1020 CHECK_NUMBER_COERCE_MARKER (limit
);
1022 i
= validate_interval_range (object
, &position
, &position
, soft
);
1023 if (NULL_INTERVAL_P (i
))
1026 here_val
= textget (i
->plist
, prop
);
1027 next
= next_interval (i
);
1028 while (! NULL_INTERVAL_P (next
)
1029 && EQ (here_val
, textget (next
->plist
, prop
))
1030 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1031 next
= next_interval (next
);
1033 if (NULL_INTERVAL_P (next
))
1036 XSETFASTINT (limit
, (STRINGP (object
)
1037 ? XSTRING (object
)->size
1038 : BUF_ZV (XBUFFER (object
))));
1039 if (!(next
->position
< XFASTINT (limit
)))
1042 return make_number (next
->position
);
1045 DEFUN ("previous-property-change", Fprevious_property_change
,
1046 Sprevious_property_change
, 1, 3, 0,
1047 doc
: /* Return the position of previous property change.
1048 Scans characters backwards from POSITION in OBJECT till it finds
1049 a change in some text property, then returns the position of the change.
1050 The optional second argument OBJECT is the string or buffer to scan.
1051 Return nil if the property is constant all the way to the start of OBJECT.
1052 If the value is non-nil, it is a position less than POSITION, never equal.
1054 If the optional third argument LIMIT is non-nil, don't search
1055 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1056 (position
, object
, limit
)
1057 Lisp_Object position
, object
, limit
;
1059 register INTERVAL i
, previous
;
1062 XSETBUFFER (object
, current_buffer
);
1065 CHECK_NUMBER_COERCE_MARKER (limit
);
1067 i
= validate_interval_range (object
, &position
, &position
, soft
);
1068 if (NULL_INTERVAL_P (i
))
1071 /* Start with the interval containing the char before point. */
1072 if (i
->position
== XFASTINT (position
))
1073 i
= previous_interval (i
);
1075 previous
= previous_interval (i
);
1076 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1078 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1079 previous
= previous_interval (previous
);
1080 if (NULL_INTERVAL_P (previous
))
1083 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1084 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1087 return make_number (previous
->position
+ LENGTH (previous
));
1090 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1091 Sprevious_single_property_change
, 2, 4, 0,
1092 doc
: /* Return the position of previous property change for a specific property.
1093 Scans characters backward from POSITION till it finds
1094 a change in the PROP property, then returns the position of the change.
1095 The optional third argument OBJECT is the string or buffer to scan.
1096 The property values are compared with `eq'.
1097 Return nil if the property is constant all the way to the start of OBJECT.
1098 If the value is non-nil, it is a position less than POSITION, never equal.
1100 If the optional fourth argument LIMIT is non-nil, don't search
1101 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1102 (position
, prop
, object
, limit
)
1103 Lisp_Object position
, prop
, object
, limit
;
1105 register INTERVAL i
, previous
;
1106 register Lisp_Object here_val
;
1109 XSETBUFFER (object
, current_buffer
);
1112 CHECK_NUMBER_COERCE_MARKER (limit
);
1114 i
= validate_interval_range (object
, &position
, &position
, soft
);
1116 /* Start with the interval containing the char before point. */
1117 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1118 i
= previous_interval (i
);
1120 if (NULL_INTERVAL_P (i
))
1123 here_val
= textget (i
->plist
, prop
);
1124 previous
= previous_interval (i
);
1125 while (!NULL_INTERVAL_P (previous
)
1126 && EQ (here_val
, textget (previous
->plist
, prop
))
1128 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1129 previous
= previous_interval (previous
);
1130 if (NULL_INTERVAL_P (previous
))
1133 XSETFASTINT (limit
, (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))));
1134 if (!(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1137 return make_number (previous
->position
+ LENGTH (previous
));
1140 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1142 DEFUN ("add-text-properties", Fadd_text_properties
,
1143 Sadd_text_properties
, 3, 4, 0,
1144 doc
: /* Add properties to the text from START to END.
1145 The third argument PROPERTIES is a property list
1146 specifying the property values to add.
1147 The optional fourth argument, OBJECT,
1148 is the string or buffer containing the text.
1149 Return t if any property value actually changed, nil otherwise. */)
1150 (start
, end
, properties
, object
)
1151 Lisp_Object start
, end
, properties
, object
;
1153 register INTERVAL i
, unchanged
;
1154 register int s
, len
, modified
= 0;
1155 struct gcpro gcpro1
;
1157 properties
= validate_plist (properties
);
1158 if (NILP (properties
))
1162 XSETBUFFER (object
, current_buffer
);
1164 i
= validate_interval_range (object
, &start
, &end
, hard
);
1165 if (NULL_INTERVAL_P (i
))
1169 len
= XINT (end
) - s
;
1171 /* No need to protect OBJECT, because we GC only if it's a buffer,
1172 and live buffers are always protected. */
1173 GCPRO1 (properties
);
1175 /* If we're not starting on an interval boundary, we have to
1176 split this interval. */
1177 if (i
->position
!= s
)
1179 /* If this interval already has the properties, we can
1181 if (interval_has_all_properties (properties
, i
))
1183 int got
= (LENGTH (i
) - (s
- i
->position
));
1185 RETURN_UNGCPRO (Qnil
);
1187 i
= next_interval (i
);
1192 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1193 copy_properties (unchanged
, i
);
1197 if (BUFFERP (object
))
1198 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1200 /* We are at the beginning of interval I, with LEN chars to scan. */
1206 if (LENGTH (i
) >= len
)
1208 /* We can UNGCPRO safely here, because there will be just
1209 one more chance to gc, in the next call to add_properties,
1210 and after that we will not need PROPERTIES or OBJECT again. */
1213 if (interval_has_all_properties (properties
, i
))
1215 if (BUFFERP (object
))
1216 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1217 XINT (end
) - XINT (start
));
1219 return modified
? Qt
: Qnil
;
1222 if (LENGTH (i
) == len
)
1224 add_properties (properties
, i
, object
);
1225 if (BUFFERP (object
))
1226 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1227 XINT (end
) - XINT (start
));
1231 /* i doesn't have the properties, and goes past the change limit */
1233 i
= split_interval_left (unchanged
, len
);
1234 copy_properties (unchanged
, i
);
1235 add_properties (properties
, i
, object
);
1236 if (BUFFERP (object
))
1237 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1238 XINT (end
) - XINT (start
));
1243 modified
+= add_properties (properties
, i
, object
);
1244 i
= next_interval (i
);
1248 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1250 DEFUN ("put-text-property", Fput_text_property
,
1251 Sput_text_property
, 4, 5, 0,
1252 doc
: /* Set one property of the text from START to END.
1253 The third and fourth arguments PROPERTY and VALUE
1254 specify the property to add.
1255 The optional fifth argument, OBJECT,
1256 is the string or buffer containing the text. */)
1257 (start
, end
, property
, value
, object
)
1258 Lisp_Object start
, end
, property
, value
, object
;
1260 Fadd_text_properties (start
, end
,
1261 Fcons (property
, Fcons (value
, Qnil
)),
1266 DEFUN ("set-text-properties", Fset_text_properties
,
1267 Sset_text_properties
, 3, 4, 0,
1268 doc
: /* Completely replace properties of text from START to END.
1269 The third argument PROPERTIES is the new property list.
1270 The optional fourth argument, OBJECT,
1271 is the string or buffer containing the text.
1272 If OBJECT is omitted or nil, it defaults to the current buffer.
1273 If PROPERTIES is nil, the effect is to remove all properties from
1274 the designated part of OBJECT. */)
1275 (start
, end
, properties
, object
)
1276 Lisp_Object start
, end
, properties
, object
;
1278 return set_text_properties (start
, end
, properties
, object
, Qt
);
1282 /* Replace properties of text from START to END with new list of
1283 properties PROPERTIES. OBJECT is the buffer or string containing
1284 the text. OBJECT nil means use the current buffer.
1285 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1286 is non-nil if properties were replaced; it is nil if there weren't
1287 any properties to replace. */
1290 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1291 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1293 register INTERVAL i
;
1294 Lisp_Object ostart
, oend
;
1299 properties
= validate_plist (properties
);
1302 XSETBUFFER (object
, current_buffer
);
1304 /* If we want no properties for a whole string,
1305 get rid of its intervals. */
1306 if (NILP (properties
) && STRINGP (object
)
1307 && XFASTINT (start
) == 0
1308 && XFASTINT (end
) == XSTRING (object
)->size
)
1310 if (! XSTRING (object
)->intervals
)
1313 XSTRING (object
)->intervals
= 0;
1317 i
= validate_interval_range (object
, &start
, &end
, soft
);
1319 if (NULL_INTERVAL_P (i
))
1321 /* If buffer has no properties, and we want none, return now. */
1322 if (NILP (properties
))
1325 /* Restore the original START and END values
1326 because validate_interval_range increments them for strings. */
1330 i
= validate_interval_range (object
, &start
, &end
, hard
);
1331 /* This can return if start == end. */
1332 if (NULL_INTERVAL_P (i
))
1336 if (BUFFERP (object
))
1337 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1339 set_text_properties_1 (start
, end
, properties
, object
, i
);
1341 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1342 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1343 XINT (end
) - XINT (start
));
1347 /* Replace properties of text from START to END with new list of
1348 properties PROPERTIES. BUFFER is the buffer containing
1349 the text. This does not obey any hooks.
1350 You can provide the interval that START is located in as I,
1351 or pass NULL for I and this function will find it.
1352 START and END can be in any order. */
1355 set_text_properties_1 (start
, end
, properties
, buffer
, i
)
1356 Lisp_Object start
, end
, properties
, buffer
;
1359 register INTERVAL prev_changed
= NULL_INTERVAL
;
1360 register int s
, len
;
1364 len
= XINT (end
) - s
;
1374 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1376 if (i
->position
!= s
)
1379 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1381 if (LENGTH (i
) > len
)
1383 copy_properties (unchanged
, i
);
1384 i
= split_interval_left (i
, len
);
1385 set_properties (properties
, i
, buffer
);
1389 set_properties (properties
, i
, buffer
);
1391 if (LENGTH (i
) == len
)
1396 i
= next_interval (i
);
1399 /* We are starting at the beginning of an interval, I */
1405 if (LENGTH (i
) >= len
)
1407 if (LENGTH (i
) > len
)
1408 i
= split_interval_left (i
, len
);
1410 /* We have to call set_properties even if we are going to
1411 merge the intervals, so as to make the undo records
1412 and cause redisplay to happen. */
1413 set_properties (properties
, i
, buffer
);
1414 if (!NULL_INTERVAL_P (prev_changed
))
1415 merge_interval_left (i
);
1421 /* We have to call set_properties even if we are going to
1422 merge the intervals, so as to make the undo records
1423 and cause redisplay to happen. */
1424 set_properties (properties
, i
, buffer
);
1425 if (NULL_INTERVAL_P (prev_changed
))
1428 prev_changed
= i
= merge_interval_left (i
);
1430 i
= next_interval (i
);
1434 DEFUN ("remove-text-properties", Fremove_text_properties
,
1435 Sremove_text_properties
, 3, 4, 0,
1436 doc
: /* Remove some properties from text from START to END.
1437 The third argument PROPERTIES is a property list
1438 whose property names specify the properties to remove.
1439 \(The values stored in PROPERTIES are ignored.)
1440 The optional fourth argument, OBJECT,
1441 is the string or buffer containing the text.
1442 Return t if any property was actually removed, nil otherwise. */)
1443 (start
, end
, properties
, object
)
1444 Lisp_Object start
, end
, properties
, object
;
1446 register INTERVAL i
, unchanged
;
1447 register int s
, len
, modified
= 0;
1450 XSETBUFFER (object
, current_buffer
);
1452 i
= validate_interval_range (object
, &start
, &end
, soft
);
1453 if (NULL_INTERVAL_P (i
))
1457 len
= XINT (end
) - s
;
1459 if (i
->position
!= s
)
1461 /* No properties on this first interval -- return if
1462 it covers the entire region. */
1463 if (! interval_has_some_properties (properties
, i
))
1465 int got
= (LENGTH (i
) - (s
- i
->position
));
1469 i
= next_interval (i
);
1471 /* Split away the beginning of this interval; what we don't
1476 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1477 copy_properties (unchanged
, i
);
1481 if (BUFFERP (object
))
1482 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1484 /* We are at the beginning of an interval, with len to scan */
1490 if (LENGTH (i
) >= len
)
1492 if (! interval_has_some_properties (properties
, i
))
1493 return modified
? Qt
: Qnil
;
1495 if (LENGTH (i
) == len
)
1497 remove_properties (properties
, Qnil
, i
, object
);
1498 if (BUFFERP (object
))
1499 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1500 XINT (end
) - XINT (start
));
1504 /* i has the properties, and goes past the change limit */
1506 i
= split_interval_left (i
, len
);
1507 copy_properties (unchanged
, i
);
1508 remove_properties (properties
, Qnil
, i
, object
);
1509 if (BUFFERP (object
))
1510 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1511 XINT (end
) - XINT (start
));
1516 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1517 i
= next_interval (i
);
1521 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1522 Sremove_list_of_text_properties
, 3, 4, 0,
1523 doc
: /* Remove some properties from text from START to END.
1524 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1525 The optional fourth argument, OBJECT,
1526 is the string or buffer containing the text, defaulting to the current buffer.
1527 Return t if any property was actually removed, nil otherwise. */)
1528 (start
, end
, list_of_properties
, object
)
1529 Lisp_Object start
, end
, list_of_properties
, object
;
1531 register INTERVAL i
, unchanged
;
1532 register int s
, len
, modified
= 0;
1533 Lisp_Object properties
;
1534 properties
= list_of_properties
;
1537 XSETBUFFER (object
, current_buffer
);
1539 i
= validate_interval_range (object
, &start
, &end
, soft
);
1540 if (NULL_INTERVAL_P (i
))
1544 len
= XINT (end
) - s
;
1546 if (i
->position
!= s
)
1548 /* No properties on this first interval -- return if
1549 it covers the entire region. */
1550 if (! interval_has_some_properties_list (properties
, i
))
1552 int got
= (LENGTH (i
) - (s
- i
->position
));
1556 i
= next_interval (i
);
1558 /* Split away the beginning of this interval; what we don't
1563 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1564 copy_properties (unchanged
, i
);
1568 if (BUFFERP (object
))
1569 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1571 /* We are at the beginning of an interval, with len to scan */
1577 if (LENGTH (i
) >= len
)
1579 if (! interval_has_some_properties_list (properties
, i
))
1580 return modified
? Qt
: Qnil
;
1582 if (LENGTH (i
) == len
)
1584 remove_properties (Qnil
, properties
, i
, object
);
1585 if (BUFFERP (object
))
1586 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1587 XINT (end
) - XINT (start
));
1591 /* i has the properties, and goes past the change limit */
1593 i
= split_interval_left (i
, len
);
1594 copy_properties (unchanged
, i
);
1595 remove_properties (Qnil
, properties
, i
, object
);
1596 if (BUFFERP (object
))
1597 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1598 XINT (end
) - XINT (start
));
1603 modified
+= remove_properties (Qnil
, properties
, i
, object
);
1604 i
= next_interval (i
);
1608 DEFUN ("text-property-any", Ftext_property_any
,
1609 Stext_property_any
, 4, 5, 0,
1610 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1611 If so, return the position of the first character whose property PROPERTY
1612 is `eq' to VALUE. Otherwise return nil.
1613 The optional fifth argument, OBJECT, is the string or buffer
1614 containing the text. */)
1615 (start
, end
, property
, value
, object
)
1616 Lisp_Object start
, end
, property
, value
, object
;
1618 register INTERVAL i
;
1619 register int e
, pos
;
1622 XSETBUFFER (object
, current_buffer
);
1623 i
= validate_interval_range (object
, &start
, &end
, soft
);
1624 if (NULL_INTERVAL_P (i
))
1625 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1628 while (! NULL_INTERVAL_P (i
))
1630 if (i
->position
>= e
)
1632 if (EQ (textget (i
->plist
, property
), value
))
1635 if (pos
< XINT (start
))
1637 return make_number (pos
);
1639 i
= next_interval (i
);
1644 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1645 Stext_property_not_all
, 4, 5, 0,
1646 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1647 If so, return the position of the first character whose property PROPERTY
1648 is not `eq' to VALUE. Otherwise, return nil.
1649 The optional fifth argument, OBJECT, is the string or buffer
1650 containing the text. */)
1651 (start
, end
, property
, value
, object
)
1652 Lisp_Object start
, end
, property
, value
, object
;
1654 register INTERVAL i
;
1658 XSETBUFFER (object
, current_buffer
);
1659 i
= validate_interval_range (object
, &start
, &end
, soft
);
1660 if (NULL_INTERVAL_P (i
))
1661 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1665 while (! NULL_INTERVAL_P (i
))
1667 if (i
->position
>= e
)
1669 if (! EQ (textget (i
->plist
, property
), value
))
1671 if (i
->position
> s
)
1673 return make_number (s
);
1675 i
= next_interval (i
);
1681 /* Return the direction from which the text-property PROP would be
1682 inherited by any new text inserted at POS: 1 if it would be
1683 inherited from the char after POS, -1 if it would be inherited from
1684 the char before POS, and 0 if from neither. */
1687 text_property_stickiness (prop
, pos
)
1691 Lisp_Object prev_pos
, front_sticky
;
1692 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1694 if (XINT (pos
) > BEGV
)
1695 /* Consider previous character. */
1697 Lisp_Object rear_non_sticky
;
1699 prev_pos
= make_number (XINT (pos
) - 1);
1700 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, Qnil
);
1702 if (!NILP (CONSP (rear_non_sticky
)
1703 ? Fmemq (prop
, rear_non_sticky
)
1705 /* PROP is rear-non-sticky. */
1709 /* Consider following character. */
1710 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, Qnil
);
1712 if (EQ (front_sticky
, Qt
)
1713 || (CONSP (front_sticky
)
1714 && !NILP (Fmemq (prop
, front_sticky
))))
1715 /* PROP is inherited from after. */
1716 is_front_sticky
= 1;
1718 /* Simple cases, where the properties are consistent. */
1719 if (is_rear_sticky
&& !is_front_sticky
)
1721 else if (!is_rear_sticky
&& is_front_sticky
)
1723 else if (!is_rear_sticky
&& !is_front_sticky
)
1726 /* The stickiness properties are inconsistent, so we have to
1727 disambiguate. Basically, rear-sticky wins, _except_ if the
1728 property that would be inherited has a value of nil, in which case
1729 front-sticky wins. */
1730 if (XINT (pos
) == BEGV
|| NILP (Fget_text_property (prev_pos
, prop
, Qnil
)))
1737 /* I don't think this is the right interface to export; how often do you
1738 want to do something like this, other than when you're copying objects
1741 I think it would be better to have a pair of functions, one which
1742 returns the text properties of a region as a list of ranges and
1743 plists, and another which applies such a list to another object. */
1745 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1746 SRC and DEST may each refer to strings or buffers.
1747 Optional sixth argument PROP causes only that property to be copied.
1748 Properties are copied to DEST as if by `add-text-properties'.
1749 Return t if any property value actually changed, nil otherwise. */
1751 /* Note this can GC when DEST is a buffer. */
1754 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1755 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1761 int s
, e
, e2
, p
, len
, modified
= 0;
1762 struct gcpro gcpro1
, gcpro2
;
1764 i
= validate_interval_range (src
, &start
, &end
, soft
);
1765 if (NULL_INTERVAL_P (i
))
1768 CHECK_NUMBER_COERCE_MARKER (pos
);
1770 Lisp_Object dest_start
, dest_end
;
1773 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1774 /* Apply this to a copy of pos; it will try to increment its arguments,
1775 which we don't want. */
1776 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1787 e2
= i
->position
+ LENGTH (i
);
1794 while (! NILP (plist
))
1796 if (EQ (Fcar (plist
), prop
))
1798 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1801 plist
= Fcdr (Fcdr (plist
));
1805 /* Must defer modifications to the interval tree in case src
1806 and dest refer to the same string or buffer. */
1807 stuff
= Fcons (Fcons (make_number (p
),
1808 Fcons (make_number (p
+ len
),
1809 Fcons (plist
, Qnil
))),
1813 i
= next_interval (i
);
1814 if (NULL_INTERVAL_P (i
))
1821 GCPRO2 (stuff
, dest
);
1823 while (! NILP (stuff
))
1826 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1827 Fcar (Fcdr (Fcdr (res
))), dest
);
1830 stuff
= Fcdr (stuff
);
1835 return modified
? Qt
: Qnil
;
1839 /* Return a list representing the text properties of OBJECT between
1840 START and END. if PROP is non-nil, report only on that property.
1841 Each result list element has the form (S E PLIST), where S and E
1842 are positions in OBJECT and PLIST is a property list containing the
1843 text properties of OBJECT between S and E. Value is nil if OBJECT
1844 doesn't contain text properties between START and END. */
1847 text_property_list (object
, start
, end
, prop
)
1848 Lisp_Object object
, start
, end
, prop
;
1855 i
= validate_interval_range (object
, &start
, &end
, soft
);
1856 if (!NULL_INTERVAL_P (i
))
1858 int s
= XINT (start
);
1863 int interval_end
, len
;
1866 interval_end
= i
->position
+ LENGTH (i
);
1867 if (interval_end
> e
)
1869 len
= interval_end
- s
;
1874 for (; !NILP (plist
); plist
= Fcdr (Fcdr (plist
)))
1875 if (EQ (Fcar (plist
), prop
))
1877 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1882 result
= Fcons (Fcons (make_number (s
),
1883 Fcons (make_number (s
+ len
),
1884 Fcons (plist
, Qnil
))),
1887 i
= next_interval (i
);
1888 if (NULL_INTERVAL_P (i
))
1898 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1899 (START END PLIST), where START and END are positions and PLIST is a
1900 property list containing the text properties to add. Adjust START
1901 and END positions by DELTA before adding properties. Value is
1902 non-zero if OBJECT was modified. */
1905 add_text_properties_from_list (object
, list
, delta
)
1906 Lisp_Object object
, list
, delta
;
1908 struct gcpro gcpro1
, gcpro2
;
1911 GCPRO2 (list
, object
);
1913 for (; CONSP (list
); list
= XCDR (list
))
1915 Lisp_Object item
, start
, end
, plist
, tem
;
1918 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1919 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1920 plist
= XCAR (XCDR (XCDR (item
)));
1922 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1933 /* Modify end-points of ranges in LIST destructively. LIST is a list
1934 as returned from text_property_list. Change end-points equal to
1935 OLD_END to NEW_END. */
1938 extend_property_ranges (list
, old_end
, new_end
)
1939 Lisp_Object list
, old_end
, new_end
;
1941 for (; CONSP (list
); list
= XCDR (list
))
1943 Lisp_Object item
, end
;
1946 end
= XCAR (XCDR (item
));
1948 if (EQ (end
, old_end
))
1949 XSETCAR (XCDR (item
), new_end
);
1955 /* Call the modification hook functions in LIST, each with START and END. */
1958 call_mod_hooks (list
, start
, end
)
1959 Lisp_Object list
, start
, end
;
1961 struct gcpro gcpro1
;
1963 while (!NILP (list
))
1965 call2 (Fcar (list
), start
, end
);
1971 /* Check for read-only intervals between character positions START ... END,
1972 in BUF, and signal an error if we find one.
1974 Then check for any modification hooks in the range.
1975 Create a list of all these hooks in lexicographic order,
1976 eliminating consecutive extra copies of the same hook. Then call
1977 those hooks in order, with START and END - 1 as arguments. */
1980 verify_interval_modification (buf
, start
, end
)
1984 register INTERVAL intervals
= BUF_INTERVALS (buf
);
1985 register INTERVAL i
;
1987 register Lisp_Object prev_mod_hooks
;
1988 Lisp_Object mod_hooks
;
1989 struct gcpro gcpro1
;
1992 prev_mod_hooks
= Qnil
;
1995 interval_insert_behind_hooks
= Qnil
;
1996 interval_insert_in_front_hooks
= Qnil
;
1998 if (NULL_INTERVAL_P (intervals
))
2008 /* For an insert operation, check the two chars around the position. */
2011 INTERVAL prev
= NULL
;
2012 Lisp_Object before
, after
;
2014 /* Set I to the interval containing the char after START,
2015 and PREV to the interval containing the char before START.
2016 Either one may be null. They may be equal. */
2017 i
= find_interval (intervals
, start
);
2019 if (start
== BUF_BEGV (buf
))
2021 else if (i
->position
== start
)
2022 prev
= previous_interval (i
);
2023 else if (i
->position
< start
)
2025 if (start
== BUF_ZV (buf
))
2028 /* If Vinhibit_read_only is set and is not a list, we can
2029 skip the read_only checks. */
2030 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2032 /* If I and PREV differ we need to check for the read-only
2033 property together with its stickiness. If either I or
2034 PREV are 0, this check is all we need.
2035 We have to take special care, since read-only may be
2036 indirectly defined via the category property. */
2039 if (! NULL_INTERVAL_P (i
))
2041 after
= textget (i
->plist
, Qread_only
);
2043 /* If interval I is read-only and read-only is
2044 front-sticky, inhibit insertion.
2045 Check for read-only as well as category. */
2047 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2051 tem
= textget (i
->plist
, Qfront_sticky
);
2052 if (TMEM (Qread_only
, tem
)
2053 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2054 && TMEM (Qcategory
, tem
)))
2059 if (! NULL_INTERVAL_P (prev
))
2061 before
= textget (prev
->plist
, Qread_only
);
2063 /* If interval PREV is read-only and read-only isn't
2064 rear-nonsticky, inhibit insertion.
2065 Check for read-only as well as category. */
2067 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2071 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2072 if (! TMEM (Qread_only
, tem
)
2073 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2074 || ! TMEM (Qcategory
, tem
)))
2079 else if (! NULL_INTERVAL_P (i
))
2081 after
= textget (i
->plist
, Qread_only
);
2083 /* If interval I is read-only and read-only is
2084 front-sticky, inhibit insertion.
2085 Check for read-only as well as category. */
2086 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2090 tem
= textget (i
->plist
, Qfront_sticky
);
2091 if (TMEM (Qread_only
, tem
)
2092 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2093 && TMEM (Qcategory
, tem
)))
2096 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2097 if (! TMEM (Qread_only
, tem
)
2098 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2099 || ! TMEM (Qcategory
, tem
)))
2105 /* Run both insert hooks (just once if they're the same). */
2106 if (!NULL_INTERVAL_P (prev
))
2107 interval_insert_behind_hooks
2108 = textget (prev
->plist
, Qinsert_behind_hooks
);
2109 if (!NULL_INTERVAL_P (i
))
2110 interval_insert_in_front_hooks
2111 = textget (i
->plist
, Qinsert_in_front_hooks
);
2115 /* Loop over intervals on or next to START...END,
2116 collecting their hooks. */
2118 i
= find_interval (intervals
, start
);
2121 if (! INTERVAL_WRITABLE_P (i
))
2124 if (!inhibit_modification_hooks
)
2126 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2127 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2129 hooks
= Fcons (mod_hooks
, hooks
);
2130 prev_mod_hooks
= mod_hooks
;
2134 i
= next_interval (i
);
2136 /* Keep going thru the interval containing the char before END. */
2137 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2139 if (!inhibit_modification_hooks
)
2142 hooks
= Fnreverse (hooks
);
2143 while (! EQ (hooks
, Qnil
))
2145 call_mod_hooks (Fcar (hooks
), make_number (start
),
2147 hooks
= Fcdr (hooks
);
2154 /* Run the interval hooks for an insertion on character range START ... END.
2155 verify_interval_modification chose which hooks to run;
2156 this function is called after the insertion happens
2157 so it can indicate the range of inserted text. */
2160 report_interval_modification (start
, end
)
2161 Lisp_Object start
, end
;
2163 if (! NILP (interval_insert_behind_hooks
))
2164 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2165 if (! NILP (interval_insert_in_front_hooks
)
2166 && ! EQ (interval_insert_in_front_hooks
,
2167 interval_insert_behind_hooks
))
2168 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2174 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
2175 doc
: /* Property-list used as default values.
2176 The value of a property in this list is seen as the value for every
2177 character that does not have its own value for that property. */);
2178 Vdefault_text_properties
= Qnil
;
2180 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
2181 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2182 This also inhibits the use of the `intangible' text property. */);
2183 Vinhibit_point_motion_hooks
= Qnil
;
2185 DEFVAR_LISP ("text-property-default-nonsticky",
2186 &Vtext_property_default_nonsticky
,
2187 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2188 Each element has the form (PROPERTY . NONSTICKINESS).
2190 If a character in a buffer has PROPERTY, new text inserted adjacent to
2191 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2192 inherits it if NONSTICKINESS is nil. The front-sticky and
2193 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2194 Vtext_property_default_nonsticky
= Qnil
;
2196 staticpro (&interval_insert_behind_hooks
);
2197 staticpro (&interval_insert_in_front_hooks
);
2198 interval_insert_behind_hooks
= Qnil
;
2199 interval_insert_in_front_hooks
= Qnil
;
2202 /* Common attributes one might give text */
2204 staticpro (&Qforeground
);
2205 Qforeground
= intern ("foreground");
2206 staticpro (&Qbackground
);
2207 Qbackground
= intern ("background");
2209 Qfont
= intern ("font");
2210 staticpro (&Qstipple
);
2211 Qstipple
= intern ("stipple");
2212 staticpro (&Qunderline
);
2213 Qunderline
= intern ("underline");
2214 staticpro (&Qread_only
);
2215 Qread_only
= intern ("read-only");
2216 staticpro (&Qinvisible
);
2217 Qinvisible
= intern ("invisible");
2218 staticpro (&Qintangible
);
2219 Qintangible
= intern ("intangible");
2220 staticpro (&Qcategory
);
2221 Qcategory
= intern ("category");
2222 staticpro (&Qlocal_map
);
2223 Qlocal_map
= intern ("local-map");
2224 staticpro (&Qfront_sticky
);
2225 Qfront_sticky
= intern ("front-sticky");
2226 staticpro (&Qrear_nonsticky
);
2227 Qrear_nonsticky
= intern ("rear-nonsticky");
2228 staticpro (&Qmouse_face
);
2229 Qmouse_face
= intern ("mouse-face");
2231 /* Properties that text might use to specify certain actions */
2233 staticpro (&Qmouse_left
);
2234 Qmouse_left
= intern ("mouse-left");
2235 staticpro (&Qmouse_entered
);
2236 Qmouse_entered
= intern ("mouse-entered");
2237 staticpro (&Qpoint_left
);
2238 Qpoint_left
= intern ("point-left");
2239 staticpro (&Qpoint_entered
);
2240 Qpoint_entered
= intern ("point-entered");
2242 defsubr (&Stext_properties_at
);
2243 defsubr (&Sget_text_property
);
2244 defsubr (&Sget_char_property
);
2245 defsubr (&Snext_char_property_change
);
2246 defsubr (&Sprevious_char_property_change
);
2247 defsubr (&Snext_single_char_property_change
);
2248 defsubr (&Sprevious_single_char_property_change
);
2249 defsubr (&Snext_property_change
);
2250 defsubr (&Snext_single_property_change
);
2251 defsubr (&Sprevious_property_change
);
2252 defsubr (&Sprevious_single_property_change
);
2253 defsubr (&Sadd_text_properties
);
2254 defsubr (&Sput_text_property
);
2255 defsubr (&Sset_text_properties
);
2256 defsubr (&Sremove_text_properties
);
2257 defsubr (&Sremove_list_of_text_properties
);
2258 defsubr (&Stext_property_any
);
2259 defsubr (&Stext_property_not_all
);
2260 /* defsubr (&Serase_text_properties); */
2261 /* defsubr (&Scopy_text_properties); */