1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
23 #include "intervals.h"
28 #define NULL (void *)0
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
41 set_properties needs to deal with the interval property cache.
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
51 Lisp_Object Qmouse_left
;
52 Lisp_Object Qmouse_entered
;
53 Lisp_Object Qpoint_left
;
54 Lisp_Object Qpoint_entered
;
55 Lisp_Object Qcategory
;
56 Lisp_Object Qlocal_map
;
58 /* Visual properties text (including strings) may have. */
59 Lisp_Object Qforeground
, Qbackground
, Qfont
, Qunderline
, Qstipple
;
60 Lisp_Object Qinvisible
, Qread_only
, Qintangible
, Qmouse_face
;
61 Lisp_Object Qminibuffer_prompt
;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
71 Lisp_Object Vinhibit_point_motion_hooks
;
72 Lisp_Object Vdefault_text_properties
;
73 Lisp_Object Vchar_property_alias_alist
;
74 Lisp_Object Vtext_property_default_nonsticky
;
76 /* verify_interval_modification saves insertion hooks here
77 to be run later by report_interval_modification. */
78 Lisp_Object interval_insert_behind_hooks
;
79 Lisp_Object interval_insert_in_front_hooks
;
81 static void text_read_only (Lisp_Object
) NO_RETURN
;
84 /* Signal a `text-read-only' error. This function makes it easier
85 to capture that error in GDB by putting a breakpoint on it. */
88 text_read_only (Lisp_Object propval
)
90 if (STRINGP (propval
))
91 xsignal1 (Qtext_read_only
, propval
);
93 xsignal0 (Qtext_read_only
);
98 /* Extract the interval at the position pointed to by BEGIN from
99 OBJECT, a string or buffer. Additionally, check that the positions
100 pointed to by BEGIN and END are within the bounds of OBJECT, and
101 reverse them if *BEGIN is greater than *END. The objects pointed
102 to by BEGIN and END may be integers or markers; if the latter, they
103 are coerced to integers.
105 When OBJECT is a string, we increment *BEGIN and *END
106 to make them origin-one.
108 Note that buffer points don't correspond to interval indices.
109 For example, point-max is 1 greater than the index of the last
110 character. This difference is handled in the caller, which uses
111 the validated points to determine a length, and operates on that.
112 Exceptions are Ftext_properties_at, Fnext_property_change, and
113 Fprevious_property_change which call this function with BEGIN == END.
114 Handle this case specially.
116 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
117 create an interval tree for OBJECT if one doesn't exist, provided
118 the object actually contains text. In the current design, if there
119 is no text, there can be no text properties. */
125 validate_interval_range (Lisp_Object object
, Lisp_Object
*begin
, Lisp_Object
*end
, int force
)
130 CHECK_STRING_OR_BUFFER (object
);
131 CHECK_NUMBER_COERCE_MARKER (*begin
);
132 CHECK_NUMBER_COERCE_MARKER (*end
);
134 /* If we are asked for a point, but from a subr which operates
135 on a range, then return nothing. */
136 if (EQ (*begin
, *end
) && begin
!= end
)
137 return NULL_INTERVAL
;
139 if (XINT (*begin
) > XINT (*end
))
147 if (BUFFERP (object
))
149 register struct buffer
*b
= XBUFFER (object
);
151 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
152 && XINT (*end
) <= BUF_ZV (b
)))
153 args_out_of_range (*begin
, *end
);
154 i
= BUF_INTERVALS (b
);
156 /* If there's no text, there are no properties. */
157 if (BUF_BEGV (b
) == BUF_ZV (b
))
158 return NULL_INTERVAL
;
160 searchpos
= XINT (*begin
);
164 EMACS_INT len
= SCHARS (object
);
166 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
167 && XINT (*end
) <= len
))
168 args_out_of_range (*begin
, *end
);
169 XSETFASTINT (*begin
, XFASTINT (*begin
));
171 XSETFASTINT (*end
, XFASTINT (*end
));
172 i
= STRING_INTERVALS (object
);
175 return NULL_INTERVAL
;
177 searchpos
= XINT (*begin
);
180 if (NULL_INTERVAL_P (i
))
181 return (force
? create_root_interval (object
) : i
);
183 return find_interval (i
, searchpos
);
186 /* Validate LIST as a property list. If LIST is not a list, then
187 make one consisting of (LIST nil). Otherwise, verify that LIST
188 is even numbered and thus suitable as a plist. */
191 validate_plist (Lisp_Object list
)
199 register Lisp_Object tail
;
200 for (i
= 0, tail
= list
; CONSP (tail
); i
++)
206 error ("Odd length text property list");
210 return Fcons (list
, Fcons (Qnil
, Qnil
));
213 /* Return nonzero if interval I has all the properties,
214 with the same values, of list PLIST. */
217 interval_has_all_properties (Lisp_Object plist
, INTERVAL i
)
219 register Lisp_Object tail1
, tail2
, sym1
;
222 /* Go through each element of PLIST. */
223 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
228 /* Go through I's plist, looking for sym1 */
229 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
230 if (EQ (sym1
, XCAR (tail2
)))
232 /* Found the same property on both lists. If the
233 values are unequal, return zero. */
234 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
237 /* Property has same value on both lists; go to next one. */
249 /* Return nonzero if the plist of interval I has any of the
250 properties of PLIST, regardless of their values. */
253 interval_has_some_properties (Lisp_Object plist
, INTERVAL i
)
255 register Lisp_Object tail1
, tail2
, sym
;
257 /* Go through each element of PLIST. */
258 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
262 /* Go through i's plist, looking for tail1 */
263 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
264 if (EQ (sym
, XCAR (tail2
)))
271 /* Return nonzero if the plist of interval I has any of the
272 property names in LIST, regardless of their values. */
275 interval_has_some_properties_list (Lisp_Object list
, INTERVAL i
)
277 register Lisp_Object tail1
, tail2
, sym
;
279 /* Go through each element of LIST. */
280 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
284 /* Go through i's plist, looking for tail1 */
285 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
286 if (EQ (sym
, XCAR (tail2
)))
293 /* Changing the plists of individual intervals. */
295 /* Return the value of PROP in property-list PLIST, or Qunbound if it
298 property_value (Lisp_Object plist
, Lisp_Object prop
)
302 while (PLIST_ELT_P (plist
, value
))
303 if (EQ (XCAR (plist
), prop
))
306 plist
= XCDR (value
);
311 /* Set the properties of INTERVAL to PROPERTIES,
312 and record undo info for the previous values.
313 OBJECT is the string or buffer that INTERVAL belongs to. */
316 set_properties (Lisp_Object properties
, INTERVAL interval
, Lisp_Object object
)
318 Lisp_Object sym
, value
;
320 if (BUFFERP (object
))
322 /* For each property in the old plist which is missing from PROPERTIES,
323 or has a different value in PROPERTIES, make an undo record. */
324 for (sym
= interval
->plist
;
325 PLIST_ELT_P (sym
, value
);
327 if (! EQ (property_value (properties
, XCAR (sym
)),
330 record_property_change (interval
->position
, LENGTH (interval
),
331 XCAR (sym
), XCAR (value
),
335 /* For each new property that has no value at all in the old plist,
336 make an undo record binding it to nil, so it will be removed. */
337 for (sym
= properties
;
338 PLIST_ELT_P (sym
, value
);
340 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
342 record_property_change (interval
->position
, LENGTH (interval
),
348 /* Store new properties. */
349 interval
->plist
= Fcopy_sequence (properties
);
352 /* Add the properties of PLIST to the interval I, or set
353 the value of I's property to the value of the property on PLIST
354 if they are different.
356 OBJECT should be the string or buffer the interval is in.
358 Return nonzero if this changes I (i.e., if any members of PLIST
359 are actually added to I's plist) */
362 add_properties (Lisp_Object plist
, INTERVAL i
, Lisp_Object object
)
364 Lisp_Object tail1
, tail2
, sym1
, val1
;
365 register int changed
= 0;
367 struct gcpro gcpro1
, gcpro2
, gcpro3
;
372 /* No need to protect OBJECT, because we can GC only in the case
373 where it is a buffer, and live buffers are always protected.
374 I and its plist are also protected, via OBJECT. */
375 GCPRO3 (tail1
, sym1
, val1
);
377 /* Go through each element of PLIST. */
378 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
381 val1
= Fcar (XCDR (tail1
));
384 /* Go through I's plist, looking for sym1 */
385 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
386 if (EQ (sym1
, XCAR (tail2
)))
388 /* No need to gcpro, because tail2 protects this
389 and it must be a cons cell (we get an error otherwise). */
390 register Lisp_Object this_cdr
;
392 this_cdr
= XCDR (tail2
);
393 /* Found the property. Now check its value. */
396 /* The properties have the same value on both lists.
397 Continue to the next property. */
398 if (EQ (val1
, Fcar (this_cdr
)))
401 /* Record this change in the buffer, for undo purposes. */
402 if (BUFFERP (object
))
404 record_property_change (i
->position
, LENGTH (i
),
405 sym1
, Fcar (this_cdr
), object
);
408 /* I's property has a different value -- change it */
409 Fsetcar (this_cdr
, val1
);
416 /* Record this change in the buffer, for undo purposes. */
417 if (BUFFERP (object
))
419 record_property_change (i
->position
, LENGTH (i
),
422 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
432 /* For any members of PLIST, or LIST,
433 which are properties of I, remove them from I's plist.
434 (If PLIST is non-nil, use that, otherwise use LIST.)
435 OBJECT is the string or buffer containing I. */
438 remove_properties (Lisp_Object plist
, Lisp_Object list
, INTERVAL i
, Lisp_Object object
)
440 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
441 register int changed
= 0;
443 /* Nonzero means tail1 is a plist, otherwise it is a list. */
446 current_plist
= i
->plist
;
449 tail1
= plist
, use_plist
= 1;
451 tail1
= list
, use_plist
= 0;
453 /* Go through each element of LIST or PLIST. */
454 while (CONSP (tail1
))
458 /* First, remove the symbol if it's at the head of the list */
459 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
461 if (BUFFERP (object
))
462 record_property_change (i
->position
, LENGTH (i
),
463 sym
, XCAR (XCDR (current_plist
)),
466 current_plist
= XCDR (XCDR (current_plist
));
470 /* Go through I's plist, looking for SYM. */
471 tail2
= current_plist
;
472 while (! NILP (tail2
))
474 register Lisp_Object
this;
475 this = XCDR (XCDR (tail2
));
476 if (CONSP (this) && EQ (sym
, XCAR (this)))
478 if (BUFFERP (object
))
479 record_property_change (i
->position
, LENGTH (i
),
480 sym
, XCAR (XCDR (this)), object
);
482 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
488 /* Advance thru TAIL1 one way or the other. */
489 tail1
= XCDR (tail1
);
490 if (use_plist
&& CONSP (tail1
))
491 tail1
= XCDR (tail1
);
495 i
->plist
= current_plist
;
500 /* Remove all properties from interval I. Return non-zero
501 if this changes the interval. */
515 /* Returns the interval of POSITION in OBJECT.
516 POSITION is BEG-based. */
519 interval_of (int position
, Lisp_Object object
)
525 XSETBUFFER (object
, current_buffer
);
526 else if (EQ (object
, Qt
))
527 return NULL_INTERVAL
;
529 CHECK_STRING_OR_BUFFER (object
);
531 if (BUFFERP (object
))
533 register struct buffer
*b
= XBUFFER (object
);
537 i
= BUF_INTERVALS (b
);
542 end
= SCHARS (object
);
543 i
= STRING_INTERVALS (object
);
546 if (!(beg
<= position
&& position
<= end
))
547 args_out_of_range (make_number (position
), make_number (position
));
548 if (beg
== end
|| NULL_INTERVAL_P (i
))
549 return NULL_INTERVAL
;
551 return find_interval (i
, position
);
554 DEFUN ("text-properties-at", Ftext_properties_at
,
555 Stext_properties_at
, 1, 2, 0,
556 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
557 If the optional second argument OBJECT is a buffer (or nil, which means
558 the current buffer), POSITION is a buffer position (integer or marker).
559 If OBJECT is a string, POSITION is a 0-based index into it.
560 If POSITION is at the end of OBJECT, the value is nil. */)
561 (Lisp_Object position
, Lisp_Object object
)
566 XSETBUFFER (object
, current_buffer
);
568 i
= validate_interval_range (object
, &position
, &position
, soft
);
569 if (NULL_INTERVAL_P (i
))
571 /* If POSITION is at the end of the interval,
572 it means it's the end of OBJECT.
573 There are no properties at the very end,
574 since no character follows. */
575 if (XINT (position
) == LENGTH (i
) + i
->position
)
581 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
582 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
583 OBJECT is optional and defaults to the current buffer.
584 If POSITION is at the end of OBJECT, the value is nil. */)
585 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
587 return textget (Ftext_properties_at (position
, object
), prop
);
590 /* Return the value of char's property PROP, in OBJECT at POSITION.
591 OBJECT is optional and defaults to the current buffer.
592 If OVERLAY is non-0, then in the case that the returned property is from
593 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
594 returned in *OVERLAY.
595 If POSITION is at the end of OBJECT, the value is nil.
596 If OBJECT is a buffer, then overlay properties are considered as well as
598 If OBJECT is a window, then that window's buffer is used, but
599 window-specific overlays are considered only if they are associated
602 get_char_property_and_overlay (Lisp_Object position
, register Lisp_Object prop
, Lisp_Object object
, Lisp_Object
*overlay
)
604 struct window
*w
= 0;
606 CHECK_NUMBER_COERCE_MARKER (position
);
609 XSETBUFFER (object
, current_buffer
);
611 if (WINDOWP (object
))
613 w
= XWINDOW (object
);
616 if (BUFFERP (object
))
619 Lisp_Object
*overlay_vec
;
620 struct buffer
*obuf
= current_buffer
;
622 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
623 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
624 xsignal1 (Qargs_out_of_range
, position
);
626 set_buffer_temp (XBUFFER (object
));
628 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
629 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
631 set_buffer_temp (obuf
);
633 /* Now check the overlays in order of decreasing priority. */
634 while (--noverlays
>= 0)
636 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
640 /* Return the overlay we got the property from. */
641 *overlay
= overlay_vec
[noverlays
];
648 /* Indicate that the return value is not from an overlay. */
651 /* Not a buffer, or no appropriate overlay, so fall through to the
653 return Fget_text_property (position
, prop
, object
);
656 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
657 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
658 Both overlay properties and text properties are checked.
659 OBJECT is optional and defaults to the current buffer.
660 If POSITION is at the end of OBJECT, the value is nil.
661 If OBJECT is a buffer, then overlay properties are considered as well as
663 If OBJECT is a window, then that window's buffer is used, but window-specific
664 overlays are considered only if they are associated with OBJECT. */)
665 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
667 return get_char_property_and_overlay (position
, prop
, object
, 0);
670 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
671 Sget_char_property_and_overlay
, 2, 3, 0,
672 doc
: /* Like `get-char-property', but with extra overlay information.
673 The value is a cons cell. Its car is the return value of `get-char-property'
674 with the same arguments--that is, the value of POSITION's property
675 PROP in OBJECT. Its cdr is the overlay in which the property was
676 found, or nil, if it was found as a text property or not found at all.
678 OBJECT is optional and defaults to the current buffer. OBJECT may be
679 a string, a buffer or a window. For strings, the cdr of the return
680 value is always nil, since strings do not have overlays. If OBJECT is
681 a window, then that window's buffer is used, but window-specific
682 overlays are considered only if they are associated with OBJECT. If
683 POSITION is at the end of OBJECT, both car and cdr are nil. */)
684 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
688 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
689 return Fcons (val
, overlay
);
693 DEFUN ("next-char-property-change", Fnext_char_property_change
,
694 Snext_char_property_change
, 1, 2, 0,
695 doc
: /* Return the position of next text property or overlay change.
696 This scans characters forward in the current buffer from POSITION till
697 it finds a change in some text property, or the beginning or end of an
698 overlay, and returns the position of that.
699 If none is found up to (point-max), the function returns (point-max).
701 If the optional second argument LIMIT is non-nil, don't search
702 past position LIMIT; return LIMIT if nothing is found before LIMIT.
703 LIMIT is a no-op if it is greater than (point-max). */)
704 (Lisp_Object position
, Lisp_Object limit
)
708 temp
= Fnext_overlay_change (position
);
711 CHECK_NUMBER_COERCE_MARKER (limit
);
712 if (XINT (limit
) < XINT (temp
))
715 return Fnext_property_change (position
, Qnil
, temp
);
718 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
719 Sprevious_char_property_change
, 1, 2, 0,
720 doc
: /* Return the position of previous text property or overlay change.
721 Scans characters backward in the current buffer from POSITION till it
722 finds a change in some text property, or the beginning or end of an
723 overlay, and returns the position of that.
724 If none is found since (point-min), the function returns (point-min).
726 If the optional second argument LIMIT is non-nil, don't search
727 past position LIMIT; return LIMIT if nothing is found before LIMIT.
728 LIMIT is a no-op if it is less than (point-min). */)
729 (Lisp_Object position
, Lisp_Object limit
)
733 temp
= Fprevious_overlay_change (position
);
736 CHECK_NUMBER_COERCE_MARKER (limit
);
737 if (XINT (limit
) > XINT (temp
))
740 return Fprevious_property_change (position
, Qnil
, temp
);
744 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
745 Snext_single_char_property_change
, 2, 4, 0,
746 doc
: /* Return the position of next text property or overlay change for a specific property.
747 Scans characters forward from POSITION till it finds
748 a change in the PROP property, then returns the position of the change.
749 If the optional third argument OBJECT is a buffer (or nil, which means
750 the current buffer), POSITION is a buffer position (integer or marker).
751 If OBJECT is a string, POSITION is a 0-based index into it.
753 In a string, scan runs to the end of the string.
754 In a buffer, it runs to (point-max), and the value cannot exceed that.
756 The property values are compared with `eq'.
757 If the property is constant all the way to the end of OBJECT, return the
758 last valid position in OBJECT.
759 If the optional fourth argument LIMIT is non-nil, don't search
760 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
761 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
763 if (STRINGP (object
))
765 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
769 position
= make_number (SCHARS (object
));
772 CHECK_NUMBER (limit
);
779 Lisp_Object initial_value
, value
;
780 int count
= SPECPDL_INDEX ();
783 CHECK_BUFFER (object
);
785 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
787 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
788 Fset_buffer (object
);
791 CHECK_NUMBER_COERCE_MARKER (position
);
793 initial_value
= Fget_char_property (position
, prop
, object
);
796 XSETFASTINT (limit
, ZV
);
798 CHECK_NUMBER_COERCE_MARKER (limit
);
800 if (XFASTINT (position
) >= XFASTINT (limit
))
803 if (XFASTINT (position
) > ZV
)
804 XSETFASTINT (position
, ZV
);
809 position
= Fnext_char_property_change (position
, limit
);
810 if (XFASTINT (position
) >= XFASTINT (limit
))
816 value
= Fget_char_property (position
, prop
, object
);
817 if (!EQ (value
, initial_value
))
821 unbind_to (count
, Qnil
);
827 DEFUN ("previous-single-char-property-change",
828 Fprevious_single_char_property_change
,
829 Sprevious_single_char_property_change
, 2, 4, 0,
830 doc
: /* Return the position of previous text property or overlay change for a specific property.
831 Scans characters backward from POSITION till it finds
832 a change in the PROP property, then returns the position of the change.
833 If the optional third argument OBJECT is a buffer (or nil, which means
834 the current buffer), POSITION is a buffer position (integer or marker).
835 If OBJECT is a string, POSITION is a 0-based index into it.
837 In a string, scan runs to the start of the string.
838 In a buffer, it runs to (point-min), and the value cannot be less than that.
840 The property values are compared with `eq'.
841 If the property is constant all the way to the start of OBJECT, return the
842 first valid position in OBJECT.
843 If the optional fourth argument LIMIT is non-nil, don't search
844 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
845 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
847 if (STRINGP (object
))
849 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
853 position
= make_number (0);
856 CHECK_NUMBER (limit
);
863 int count
= SPECPDL_INDEX ();
866 CHECK_BUFFER (object
);
868 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
870 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
871 Fset_buffer (object
);
874 CHECK_NUMBER_COERCE_MARKER (position
);
877 XSETFASTINT (limit
, BEGV
);
879 CHECK_NUMBER_COERCE_MARKER (limit
);
881 if (XFASTINT (position
) <= XFASTINT (limit
))
884 if (XFASTINT (position
) < BEGV
)
885 XSETFASTINT (position
, BEGV
);
889 Lisp_Object initial_value
890 = Fget_char_property (make_number (XFASTINT (position
) - 1),
895 position
= Fprevious_char_property_change (position
, limit
);
897 if (XFASTINT (position
) <= XFASTINT (limit
))
905 = Fget_char_property (make_number (XFASTINT (position
) - 1),
908 if (!EQ (value
, initial_value
))
914 unbind_to (count
, Qnil
);
920 DEFUN ("next-property-change", Fnext_property_change
,
921 Snext_property_change
, 1, 3, 0,
922 doc
: /* Return the position of next property change.
923 Scans characters forward from POSITION in OBJECT till it finds
924 a change in some text property, then returns the position of the change.
925 If the optional second argument OBJECT is a buffer (or nil, which means
926 the current buffer), POSITION is a buffer position (integer or marker).
927 If OBJECT is a string, POSITION is a 0-based index into it.
928 Return nil if the property is constant all the way to the end of OBJECT.
929 If the value is non-nil, it is a position greater than POSITION, never equal.
931 If the optional third argument LIMIT is non-nil, don't search
932 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
933 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
935 register INTERVAL i
, next
;
938 XSETBUFFER (object
, current_buffer
);
940 if (!NILP (limit
) && !EQ (limit
, Qt
))
941 CHECK_NUMBER_COERCE_MARKER (limit
);
943 i
= validate_interval_range (object
, &position
, &position
, soft
);
945 /* If LIMIT is t, return start of next interval--don't
946 bother checking further intervals. */
949 if (NULL_INTERVAL_P (i
))
952 next
= next_interval (i
);
954 if (NULL_INTERVAL_P (next
))
955 XSETFASTINT (position
, (STRINGP (object
)
957 : BUF_ZV (XBUFFER (object
))));
959 XSETFASTINT (position
, next
->position
);
963 if (NULL_INTERVAL_P (i
))
966 next
= next_interval (i
);
968 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
969 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
970 next
= next_interval (next
);
972 if (NULL_INTERVAL_P (next
)
978 : BUF_ZV (XBUFFER (object
))))))
981 return make_number (next
->position
);
984 /* Return 1 if there's a change in some property between BEG and END. */
987 property_change_between_p (EMACS_INT beg
, EMACS_INT end
)
989 register INTERVAL i
, next
;
990 Lisp_Object object
, pos
;
992 XSETBUFFER (object
, current_buffer
);
993 XSETFASTINT (pos
, beg
);
995 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
996 if (NULL_INTERVAL_P (i
))
999 next
= next_interval (i
);
1000 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
1002 next
= next_interval (next
);
1003 if (NULL_INTERVAL_P (next
))
1005 if (next
->position
>= end
)
1009 if (NULL_INTERVAL_P (next
))
1015 DEFUN ("next-single-property-change", Fnext_single_property_change
,
1016 Snext_single_property_change
, 2, 4, 0,
1017 doc
: /* Return the position of next property change for a specific property.
1018 Scans characters forward from POSITION till it finds
1019 a change in the PROP property, then returns the position of the change.
1020 If the optional third argument OBJECT is a buffer (or nil, which means
1021 the current buffer), POSITION is a buffer position (integer or marker).
1022 If OBJECT is a string, POSITION is a 0-based index into it.
1023 The property values are compared with `eq'.
1024 Return nil if the property is constant all the way to the end of OBJECT.
1025 If the value is non-nil, it is a position greater than POSITION, never equal.
1027 If the optional fourth argument LIMIT is non-nil, don't search
1028 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1029 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1031 register INTERVAL i
, next
;
1032 register Lisp_Object here_val
;
1035 XSETBUFFER (object
, current_buffer
);
1038 CHECK_NUMBER_COERCE_MARKER (limit
);
1040 i
= validate_interval_range (object
, &position
, &position
, soft
);
1041 if (NULL_INTERVAL_P (i
))
1044 here_val
= textget (i
->plist
, prop
);
1045 next
= next_interval (i
);
1046 while (! NULL_INTERVAL_P (next
)
1047 && EQ (here_val
, textget (next
->plist
, prop
))
1048 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1049 next
= next_interval (next
);
1051 if (NULL_INTERVAL_P (next
)
1053 >= (INTEGERP (limit
)
1057 : BUF_ZV (XBUFFER (object
))))))
1060 return make_number (next
->position
);
1063 DEFUN ("previous-property-change", Fprevious_property_change
,
1064 Sprevious_property_change
, 1, 3, 0,
1065 doc
: /* Return the position of previous property change.
1066 Scans characters backwards from POSITION in OBJECT till it finds
1067 a change in some text property, then returns the position of the change.
1068 If the optional second argument OBJECT is a buffer (or nil, which means
1069 the current buffer), POSITION is a buffer position (integer or marker).
1070 If OBJECT is a string, POSITION is a 0-based index into it.
1071 Return nil if the property is constant all the way to the start of OBJECT.
1072 If the value is non-nil, it is a position less than POSITION, never equal.
1074 If the optional third argument LIMIT is non-nil, don't search
1075 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1076 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
1078 register INTERVAL i
, previous
;
1081 XSETBUFFER (object
, current_buffer
);
1084 CHECK_NUMBER_COERCE_MARKER (limit
);
1086 i
= validate_interval_range (object
, &position
, &position
, soft
);
1087 if (NULL_INTERVAL_P (i
))
1090 /* Start with the interval containing the char before point. */
1091 if (i
->position
== XFASTINT (position
))
1092 i
= previous_interval (i
);
1094 previous
= previous_interval (i
);
1095 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1097 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1098 previous
= previous_interval (previous
);
1100 if (NULL_INTERVAL_P (previous
)
1101 || (previous
->position
+ LENGTH (previous
)
1102 <= (INTEGERP (limit
)
1104 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1107 return make_number (previous
->position
+ LENGTH (previous
));
1110 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1111 Sprevious_single_property_change
, 2, 4, 0,
1112 doc
: /* Return the position of previous property change for a specific property.
1113 Scans characters backward from POSITION till it finds
1114 a change in the PROP property, then returns the position of the change.
1115 If the optional third argument OBJECT is a buffer (or nil, which means
1116 the current buffer), POSITION is a buffer position (integer or marker).
1117 If OBJECT is a string, POSITION is a 0-based index into it.
1118 The property values are compared with `eq'.
1119 Return nil if the property is constant all the way to the start of OBJECT.
1120 If the value is non-nil, it is a position less than POSITION, never equal.
1122 If the optional fourth argument LIMIT is non-nil, don't search
1123 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1124 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1126 register INTERVAL i
, previous
;
1127 register Lisp_Object here_val
;
1130 XSETBUFFER (object
, current_buffer
);
1133 CHECK_NUMBER_COERCE_MARKER (limit
);
1135 i
= validate_interval_range (object
, &position
, &position
, soft
);
1137 /* Start with the interval containing the char before point. */
1138 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1139 i
= previous_interval (i
);
1141 if (NULL_INTERVAL_P (i
))
1144 here_val
= textget (i
->plist
, prop
);
1145 previous
= previous_interval (i
);
1146 while (!NULL_INTERVAL_P (previous
)
1147 && EQ (here_val
, textget (previous
->plist
, prop
))
1149 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1150 previous
= previous_interval (previous
);
1152 if (NULL_INTERVAL_P (previous
)
1153 || (previous
->position
+ LENGTH (previous
)
1154 <= (INTEGERP (limit
)
1156 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1159 return make_number (previous
->position
+ LENGTH (previous
));
1162 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1164 DEFUN ("add-text-properties", Fadd_text_properties
,
1165 Sadd_text_properties
, 3, 4, 0,
1166 doc
: /* Add properties to the text from START to END.
1167 The third argument PROPERTIES is a property list
1168 specifying the property values to add. If the optional fourth argument
1169 OBJECT is a buffer (or nil, which means the current buffer),
1170 START and END are buffer positions (integers or markers).
1171 If OBJECT is a string, START and END are 0-based indices into it.
1172 Return t if any property value actually changed, nil otherwise. */)
1173 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1175 register INTERVAL i
, unchanged
;
1176 register EMACS_INT s
, len
;
1177 register int modified
= 0;
1178 struct gcpro gcpro1
;
1180 properties
= validate_plist (properties
);
1181 if (NILP (properties
))
1185 XSETBUFFER (object
, current_buffer
);
1187 i
= validate_interval_range (object
, &start
, &end
, hard
);
1188 if (NULL_INTERVAL_P (i
))
1192 len
= XINT (end
) - s
;
1194 /* No need to protect OBJECT, because we GC only if it's a buffer,
1195 and live buffers are always protected. */
1196 GCPRO1 (properties
);
1198 /* If we're not starting on an interval boundary, we have to
1199 split this interval. */
1200 if (i
->position
!= s
)
1202 /* If this interval already has the properties, we can
1204 if (interval_has_all_properties (properties
, i
))
1206 EMACS_INT got
= (LENGTH (i
) - (s
- i
->position
));
1208 RETURN_UNGCPRO (Qnil
);
1210 i
= next_interval (i
);
1215 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1216 copy_properties (unchanged
, i
);
1220 if (BUFFERP (object
))
1221 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1223 /* We are at the beginning of interval I, with LEN chars to scan. */
1229 if (LENGTH (i
) >= len
)
1231 /* We can UNGCPRO safely here, because there will be just
1232 one more chance to gc, in the next call to add_properties,
1233 and after that we will not need PROPERTIES or OBJECT again. */
1236 if (interval_has_all_properties (properties
, i
))
1238 if (BUFFERP (object
))
1239 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1240 XINT (end
) - XINT (start
));
1242 return modified
? Qt
: Qnil
;
1245 if (LENGTH (i
) == len
)
1247 add_properties (properties
, i
, object
);
1248 if (BUFFERP (object
))
1249 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1250 XINT (end
) - XINT (start
));
1254 /* i doesn't have the properties, and goes past the change limit */
1256 i
= split_interval_left (unchanged
, len
);
1257 copy_properties (unchanged
, i
);
1258 add_properties (properties
, i
, object
);
1259 if (BUFFERP (object
))
1260 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1261 XINT (end
) - XINT (start
));
1266 modified
+= add_properties (properties
, i
, object
);
1267 i
= next_interval (i
);
1271 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1273 DEFUN ("put-text-property", Fput_text_property
,
1274 Sput_text_property
, 4, 5, 0,
1275 doc
: /* Set one property of the text from START to END.
1276 The third and fourth arguments PROPERTY and VALUE
1277 specify the property to add.
1278 If the optional fifth argument OBJECT is a buffer (or nil, which means
1279 the current buffer), START and END are buffer positions (integers or
1280 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1281 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1283 Fadd_text_properties (start
, end
,
1284 Fcons (property
, Fcons (value
, Qnil
)),
1289 DEFUN ("set-text-properties", Fset_text_properties
,
1290 Sset_text_properties
, 3, 4, 0,
1291 doc
: /* Completely replace properties of text from START to END.
1292 The third argument PROPERTIES is the new property list.
1293 If the optional fourth argument OBJECT is a buffer (or nil, which means
1294 the current buffer), START and END are buffer positions (integers or
1295 markers). If OBJECT is a string, START and END are 0-based indices into it.
1296 If PROPERTIES is nil, the effect is to remove all properties from
1297 the designated part of OBJECT. */)
1298 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1300 return set_text_properties (start
, end
, properties
, object
, Qt
);
1304 /* Replace properties of text from START to END with new list of
1305 properties PROPERTIES. OBJECT is the buffer or string containing
1306 the text. OBJECT nil means use the current buffer.
1307 COHERENT_CHANGE_P nil means this is being called as an internal
1308 subroutine, rather than as a change primitive with checking of
1309 read-only, invoking change hooks, etc.. Value is nil if the
1310 function _detected_ that it did not replace any properties, non-nil
1314 set_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
, Lisp_Object coherent_change_p
)
1316 register INTERVAL i
;
1317 Lisp_Object ostart
, oend
;
1322 properties
= validate_plist (properties
);
1325 XSETBUFFER (object
, current_buffer
);
1327 /* If we want no properties for a whole string,
1328 get rid of its intervals. */
1329 if (NILP (properties
) && STRINGP (object
)
1330 && XFASTINT (start
) == 0
1331 && XFASTINT (end
) == SCHARS (object
))
1333 if (! STRING_INTERVALS (object
))
1336 STRING_SET_INTERVALS (object
, NULL_INTERVAL
);
1340 i
= validate_interval_range (object
, &start
, &end
, soft
);
1342 if (NULL_INTERVAL_P (i
))
1344 /* If buffer has no properties, and we want none, return now. */
1345 if (NILP (properties
))
1348 /* Restore the original START and END values
1349 because validate_interval_range increments them for strings. */
1353 i
= validate_interval_range (object
, &start
, &end
, hard
);
1354 /* This can return if start == end. */
1355 if (NULL_INTERVAL_P (i
))
1359 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1360 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1362 set_text_properties_1 (start
, end
, properties
, object
, i
);
1364 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1365 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1366 XINT (end
) - XINT (start
));
1370 /* Replace properties of text from START to END with new list of
1371 properties PROPERTIES. BUFFER is the buffer containing
1372 the text. This does not obey any hooks.
1373 You can provide the interval that START is located in as I,
1374 or pass NULL for I and this function will find it.
1375 START and END can be in any order. */
1378 set_text_properties_1 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object buffer
, INTERVAL i
)
1380 register INTERVAL prev_changed
= NULL_INTERVAL
;
1381 register EMACS_INT s
, len
;
1385 len
= XINT (end
) - s
;
1395 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1397 if (i
->position
!= s
)
1400 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1402 if (LENGTH (i
) > len
)
1404 copy_properties (unchanged
, i
);
1405 i
= split_interval_left (i
, len
);
1406 set_properties (properties
, i
, buffer
);
1410 set_properties (properties
, i
, buffer
);
1412 if (LENGTH (i
) == len
)
1417 i
= next_interval (i
);
1420 /* We are starting at the beginning of an interval, I */
1426 if (LENGTH (i
) >= len
)
1428 if (LENGTH (i
) > len
)
1429 i
= split_interval_left (i
, len
);
1431 /* We have to call set_properties even if we are going to
1432 merge the intervals, so as to make the undo records
1433 and cause redisplay to happen. */
1434 set_properties (properties
, i
, buffer
);
1435 if (!NULL_INTERVAL_P (prev_changed
))
1436 merge_interval_left (i
);
1442 /* We have to call set_properties even if we are going to
1443 merge the intervals, so as to make the undo records
1444 and cause redisplay to happen. */
1445 set_properties (properties
, i
, buffer
);
1446 if (NULL_INTERVAL_P (prev_changed
))
1449 prev_changed
= i
= merge_interval_left (i
);
1451 i
= next_interval (i
);
1455 DEFUN ("remove-text-properties", Fremove_text_properties
,
1456 Sremove_text_properties
, 3, 4, 0,
1457 doc
: /* Remove some properties from text from START to END.
1458 The third argument PROPERTIES is a property list
1459 whose property names specify the properties to remove.
1460 \(The values stored in PROPERTIES are ignored.)
1461 If the optional fourth argument OBJECT is a buffer (or nil, which means
1462 the current buffer), START and END are buffer positions (integers or
1463 markers). If OBJECT is a string, START and END are 0-based indices into it.
1464 Return t if any property was actually removed, nil otherwise.
1466 Use `set-text-properties' if you want to remove all text properties. */)
1467 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1469 register INTERVAL i
, unchanged
;
1470 register EMACS_INT s
, len
;
1471 register int modified
= 0;
1474 XSETBUFFER (object
, current_buffer
);
1476 i
= validate_interval_range (object
, &start
, &end
, soft
);
1477 if (NULL_INTERVAL_P (i
))
1481 len
= XINT (end
) - s
;
1483 if (i
->position
!= s
)
1485 /* No properties on this first interval -- return if
1486 it covers the entire region. */
1487 if (! interval_has_some_properties (properties
, i
))
1489 EMACS_INT got
= (LENGTH (i
) - (s
- i
->position
));
1493 i
= next_interval (i
);
1495 /* Split away the beginning of this interval; what we don't
1500 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1501 copy_properties (unchanged
, i
);
1505 if (BUFFERP (object
))
1506 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1508 /* We are at the beginning of an interval, with len to scan */
1514 if (LENGTH (i
) >= len
)
1516 if (! interval_has_some_properties (properties
, i
))
1517 return modified
? Qt
: Qnil
;
1519 if (LENGTH (i
) == len
)
1521 remove_properties (properties
, Qnil
, i
, object
);
1522 if (BUFFERP (object
))
1523 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1524 XINT (end
) - XINT (start
));
1528 /* i has the properties, and goes past the change limit */
1530 i
= split_interval_left (i
, len
);
1531 copy_properties (unchanged
, i
);
1532 remove_properties (properties
, Qnil
, i
, object
);
1533 if (BUFFERP (object
))
1534 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1535 XINT (end
) - XINT (start
));
1540 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1541 i
= next_interval (i
);
1545 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1546 Sremove_list_of_text_properties
, 3, 4, 0,
1547 doc
: /* Remove some properties from text from START to END.
1548 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1549 If the optional fourth argument OBJECT is a buffer (or nil, which means
1550 the current buffer), START and END are buffer positions (integers or
1551 markers). If OBJECT is a string, START and END are 0-based indices into it.
1552 Return t if any property was actually removed, nil otherwise. */)
1553 (Lisp_Object start
, Lisp_Object end
, Lisp_Object list_of_properties
, Lisp_Object object
)
1555 register INTERVAL i
, unchanged
;
1556 register EMACS_INT s
, len
;
1557 register int modified
= 0;
1558 Lisp_Object properties
;
1559 properties
= list_of_properties
;
1562 XSETBUFFER (object
, current_buffer
);
1564 i
= validate_interval_range (object
, &start
, &end
, soft
);
1565 if (NULL_INTERVAL_P (i
))
1569 len
= XINT (end
) - s
;
1571 if (i
->position
!= s
)
1573 /* No properties on this first interval -- return if
1574 it covers the entire region. */
1575 if (! interval_has_some_properties_list (properties
, i
))
1577 EMACS_INT got
= (LENGTH (i
) - (s
- i
->position
));
1581 i
= next_interval (i
);
1583 /* Split away the beginning of this interval; what we don't
1588 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1589 copy_properties (unchanged
, i
);
1593 /* We are at the beginning of an interval, with len to scan.
1594 The flag `modified' records if changes have been made.
1595 When object is a buffer, we must call modify_region before changes are
1596 made and signal_after_change when we are done.
1597 We call modify_region before calling remove_properties if modified == 0,
1598 and we call signal_after_change before returning if modified != 0. */
1604 if (LENGTH (i
) >= len
)
1606 if (! interval_has_some_properties_list (properties
, i
))
1609 if (BUFFERP (object
))
1610 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1611 XINT (end
) - XINT (start
));
1617 if (LENGTH (i
) == len
)
1619 if (!modified
&& BUFFERP (object
))
1620 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1621 remove_properties (Qnil
, properties
, i
, object
);
1622 if (BUFFERP (object
))
1623 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1624 XINT (end
) - XINT (start
));
1628 /* i has the properties, and goes past the change limit */
1630 i
= split_interval_left (i
, len
);
1631 copy_properties (unchanged
, i
);
1632 if (!modified
&& BUFFERP (object
))
1633 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1634 remove_properties (Qnil
, properties
, i
, object
);
1635 if (BUFFERP (object
))
1636 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1637 XINT (end
) - XINT (start
));
1641 if (interval_has_some_properties_list (properties
, i
))
1643 if (!modified
&& BUFFERP (object
))
1644 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1645 remove_properties (Qnil
, properties
, i
, object
);
1649 i
= next_interval (i
);
1653 DEFUN ("text-property-any", Ftext_property_any
,
1654 Stext_property_any
, 4, 5, 0,
1655 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1656 If so, return the position of the first character whose property PROPERTY
1657 is `eq' to VALUE. Otherwise return nil.
1658 If the optional fifth argument OBJECT is a buffer (or nil, which means
1659 the current buffer), START and END are buffer positions (integers or
1660 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1661 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1663 register INTERVAL i
;
1664 register EMACS_INT e
, pos
;
1667 XSETBUFFER (object
, current_buffer
);
1668 i
= validate_interval_range (object
, &start
, &end
, soft
);
1669 if (NULL_INTERVAL_P (i
))
1670 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1673 while (! NULL_INTERVAL_P (i
))
1675 if (i
->position
>= e
)
1677 if (EQ (textget (i
->plist
, property
), value
))
1680 if (pos
< XINT (start
))
1682 return make_number (pos
);
1684 i
= next_interval (i
);
1689 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1690 Stext_property_not_all
, 4, 5, 0,
1691 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1692 If so, return the position of the first character whose property PROPERTY
1693 is not `eq' to VALUE. Otherwise, return nil.
1694 If the optional fifth argument OBJECT is a buffer (or nil, which means
1695 the current buffer), START and END are buffer positions (integers or
1696 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1697 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1699 register INTERVAL i
;
1700 register EMACS_INT s
, e
;
1703 XSETBUFFER (object
, current_buffer
);
1704 i
= validate_interval_range (object
, &start
, &end
, soft
);
1705 if (NULL_INTERVAL_P (i
))
1706 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1710 while (! NULL_INTERVAL_P (i
))
1712 if (i
->position
>= e
)
1714 if (! EQ (textget (i
->plist
, property
), value
))
1716 if (i
->position
> s
)
1718 return make_number (s
);
1720 i
= next_interval (i
);
1726 /* Return the direction from which the text-property PROP would be
1727 inherited by any new text inserted at POS: 1 if it would be
1728 inherited from the char after POS, -1 if it would be inherited from
1729 the char before POS, and 0 if from neither.
1730 BUFFER can be either a buffer or nil (meaning current buffer). */
1733 text_property_stickiness (Lisp_Object prop
, Lisp_Object pos
, Lisp_Object buffer
)
1735 Lisp_Object prev_pos
, front_sticky
;
1736 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1739 XSETBUFFER (buffer
, current_buffer
);
1741 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1742 /* Consider previous character. */
1744 Lisp_Object rear_non_sticky
;
1746 prev_pos
= make_number (XINT (pos
) - 1);
1747 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1749 if (!NILP (CONSP (rear_non_sticky
)
1750 ? Fmemq (prop
, rear_non_sticky
)
1752 /* PROP is rear-non-sticky. */
1758 /* Consider following character. */
1759 /* This signals an arg-out-of-range error if pos is outside the
1760 buffer's accessible range. */
1761 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1763 if (EQ (front_sticky
, Qt
)
1764 || (CONSP (front_sticky
)
1765 && !NILP (Fmemq (prop
, front_sticky
))))
1766 /* PROP is inherited from after. */
1767 is_front_sticky
= 1;
1769 /* Simple cases, where the properties are consistent. */
1770 if (is_rear_sticky
&& !is_front_sticky
)
1772 else if (!is_rear_sticky
&& is_front_sticky
)
1774 else if (!is_rear_sticky
&& !is_front_sticky
)
1777 /* The stickiness properties are inconsistent, so we have to
1778 disambiguate. Basically, rear-sticky wins, _except_ if the
1779 property that would be inherited has a value of nil, in which case
1780 front-sticky wins. */
1781 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1782 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1789 /* I don't think this is the right interface to export; how often do you
1790 want to do something like this, other than when you're copying objects
1793 I think it would be better to have a pair of functions, one which
1794 returns the text properties of a region as a list of ranges and
1795 plists, and another which applies such a list to another object. */
1797 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1798 SRC and DEST may each refer to strings or buffers.
1799 Optional sixth argument PROP causes only that property to be copied.
1800 Properties are copied to DEST as if by `add-text-properties'.
1801 Return t if any property value actually changed, nil otherwise. */
1803 /* Note this can GC when DEST is a buffer. */
1806 copy_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object src
, Lisp_Object pos
, Lisp_Object dest
, Lisp_Object prop
)
1812 EMACS_INT s
, e
, e2
, p
, len
;
1814 struct gcpro gcpro1
, gcpro2
;
1816 i
= validate_interval_range (src
, &start
, &end
, soft
);
1817 if (NULL_INTERVAL_P (i
))
1820 CHECK_NUMBER_COERCE_MARKER (pos
);
1822 Lisp_Object dest_start
, dest_end
;
1825 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1826 /* Apply this to a copy of pos; it will try to increment its arguments,
1827 which we don't want. */
1828 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1839 e2
= i
->position
+ LENGTH (i
);
1846 while (! NILP (plist
))
1848 if (EQ (Fcar (plist
), prop
))
1850 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1853 plist
= Fcdr (Fcdr (plist
));
1857 /* Must defer modifications to the interval tree in case src
1858 and dest refer to the same string or buffer. */
1859 stuff
= Fcons (Fcons (make_number (p
),
1860 Fcons (make_number (p
+ len
),
1861 Fcons (plist
, Qnil
))),
1865 i
= next_interval (i
);
1866 if (NULL_INTERVAL_P (i
))
1873 GCPRO2 (stuff
, dest
);
1875 while (! NILP (stuff
))
1878 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1879 Fcar (Fcdr (Fcdr (res
))), dest
);
1882 stuff
= Fcdr (stuff
);
1887 return modified
? Qt
: Qnil
;
1891 /* Return a list representing the text properties of OBJECT between
1892 START and END. if PROP is non-nil, report only on that property.
1893 Each result list element has the form (S E PLIST), where S and E
1894 are positions in OBJECT and PLIST is a property list containing the
1895 text properties of OBJECT between S and E. Value is nil if OBJECT
1896 doesn't contain text properties between START and END. */
1899 text_property_list (Lisp_Object object
, Lisp_Object start
, Lisp_Object end
, Lisp_Object prop
)
1906 i
= validate_interval_range (object
, &start
, &end
, soft
);
1907 if (!NULL_INTERVAL_P (i
))
1909 EMACS_INT s
= XINT (start
);
1910 EMACS_INT e
= XINT (end
);
1914 EMACS_INT interval_end
, len
;
1917 interval_end
= i
->position
+ LENGTH (i
);
1918 if (interval_end
> e
)
1920 len
= interval_end
- s
;
1925 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1926 if (EQ (XCAR (plist
), prop
))
1928 plist
= Fcons (prop
, Fcons (Fcar (XCDR (plist
)), Qnil
));
1933 result
= Fcons (Fcons (make_number (s
),
1934 Fcons (make_number (s
+ len
),
1935 Fcons (plist
, Qnil
))),
1938 i
= next_interval (i
);
1939 if (NULL_INTERVAL_P (i
))
1949 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1950 (START END PLIST), where START and END are positions and PLIST is a
1951 property list containing the text properties to add. Adjust START
1952 and END positions by DELTA before adding properties. Value is
1953 non-zero if OBJECT was modified. */
1956 add_text_properties_from_list (Lisp_Object object
, Lisp_Object list
, Lisp_Object delta
)
1958 struct gcpro gcpro1
, gcpro2
;
1961 GCPRO2 (list
, object
);
1963 for (; CONSP (list
); list
= XCDR (list
))
1965 Lisp_Object item
, start
, end
, plist
, tem
;
1968 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1969 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1970 plist
= XCAR (XCDR (XCDR (item
)));
1972 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1983 /* Modify end-points of ranges in LIST destructively, and return the
1984 new list. LIST is a list as returned from text_property_list.
1985 Discard properties that begin at or after NEW_END, and limit
1986 end-points to NEW_END. */
1989 extend_property_ranges (Lisp_Object list
, Lisp_Object new_end
)
1991 Lisp_Object prev
= Qnil
, head
= list
;
1992 EMACS_INT max
= XINT (new_end
);
1994 for (; CONSP (list
); prev
= list
, list
= XCDR (list
))
1996 Lisp_Object item
, beg
, end
;
2000 end
= XCAR (XCDR (item
));
2002 if (XINT (beg
) >= max
)
2004 /* The start-point is past the end of the new string.
2005 Discard this property. */
2006 if (EQ (head
, list
))
2009 XSETCDR (prev
, XCDR (list
));
2011 else if (XINT (end
) > max
)
2012 /* The end-point is past the end of the new string. */
2013 XSETCAR (XCDR (item
), new_end
);
2021 /* Call the modification hook functions in LIST, each with START and END. */
2024 call_mod_hooks (Lisp_Object list
, Lisp_Object start
, Lisp_Object end
)
2026 struct gcpro gcpro1
;
2028 while (!NILP (list
))
2030 call2 (Fcar (list
), start
, end
);
2036 /* Check for read-only intervals between character positions START ... END,
2037 in BUF, and signal an error if we find one.
2039 Then check for any modification hooks in the range.
2040 Create a list of all these hooks in lexicographic order,
2041 eliminating consecutive extra copies of the same hook. Then call
2042 those hooks in order, with START and END - 1 as arguments. */
2045 verify_interval_modification (struct buffer
*buf
, int start
, int end
)
2047 register INTERVAL intervals
= BUF_INTERVALS (buf
);
2048 register INTERVAL i
;
2050 register Lisp_Object prev_mod_hooks
;
2051 Lisp_Object mod_hooks
;
2052 struct gcpro gcpro1
;
2055 prev_mod_hooks
= Qnil
;
2058 interval_insert_behind_hooks
= Qnil
;
2059 interval_insert_in_front_hooks
= Qnil
;
2061 if (NULL_INTERVAL_P (intervals
))
2066 EMACS_INT temp
= start
;
2071 /* For an insert operation, check the two chars around the position. */
2074 INTERVAL prev
= NULL
;
2075 Lisp_Object before
, after
;
2077 /* Set I to the interval containing the char after START,
2078 and PREV to the interval containing the char before START.
2079 Either one may be null. They may be equal. */
2080 i
= find_interval (intervals
, start
);
2082 if (start
== BUF_BEGV (buf
))
2084 else if (i
->position
== start
)
2085 prev
= previous_interval (i
);
2086 else if (i
->position
< start
)
2088 if (start
== BUF_ZV (buf
))
2091 /* If Vinhibit_read_only is set and is not a list, we can
2092 skip the read_only checks. */
2093 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2095 /* If I and PREV differ we need to check for the read-only
2096 property together with its stickiness. If either I or
2097 PREV are 0, this check is all we need.
2098 We have to take special care, since read-only may be
2099 indirectly defined via the category property. */
2102 if (! NULL_INTERVAL_P (i
))
2104 after
= textget (i
->plist
, Qread_only
);
2106 /* If interval I is read-only and read-only is
2107 front-sticky, inhibit insertion.
2108 Check for read-only as well as category. */
2110 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2114 tem
= textget (i
->plist
, Qfront_sticky
);
2115 if (TMEM (Qread_only
, tem
)
2116 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2117 && TMEM (Qcategory
, tem
)))
2118 text_read_only (after
);
2122 if (! NULL_INTERVAL_P (prev
))
2124 before
= textget (prev
->plist
, Qread_only
);
2126 /* If interval PREV is read-only and read-only isn't
2127 rear-nonsticky, inhibit insertion.
2128 Check for read-only as well as category. */
2130 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2134 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2135 if (! TMEM (Qread_only
, tem
)
2136 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2137 || ! TMEM (Qcategory
, tem
)))
2138 text_read_only (before
);
2142 else if (! NULL_INTERVAL_P (i
))
2144 after
= textget (i
->plist
, Qread_only
);
2146 /* If interval I is read-only and read-only is
2147 front-sticky, inhibit insertion.
2148 Check for read-only as well as category. */
2149 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2153 tem
= textget (i
->plist
, Qfront_sticky
);
2154 if (TMEM (Qread_only
, tem
)
2155 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2156 && TMEM (Qcategory
, tem
)))
2157 text_read_only (after
);
2159 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2160 if (! TMEM (Qread_only
, tem
)
2161 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2162 || ! TMEM (Qcategory
, tem
)))
2163 text_read_only (after
);
2168 /* Run both insert hooks (just once if they're the same). */
2169 if (!NULL_INTERVAL_P (prev
))
2170 interval_insert_behind_hooks
2171 = textget (prev
->plist
, Qinsert_behind_hooks
);
2172 if (!NULL_INTERVAL_P (i
))
2173 interval_insert_in_front_hooks
2174 = textget (i
->plist
, Qinsert_in_front_hooks
);
2178 /* Loop over intervals on or next to START...END,
2179 collecting their hooks. */
2181 i
= find_interval (intervals
, start
);
2184 if (! INTERVAL_WRITABLE_P (i
))
2185 text_read_only (textget (i
->plist
, Qread_only
));
2187 if (!inhibit_modification_hooks
)
2189 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2190 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2192 hooks
= Fcons (mod_hooks
, hooks
);
2193 prev_mod_hooks
= mod_hooks
;
2197 i
= next_interval (i
);
2199 /* Keep going thru the interval containing the char before END. */
2200 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2202 if (!inhibit_modification_hooks
)
2205 hooks
= Fnreverse (hooks
);
2206 while (! EQ (hooks
, Qnil
))
2208 call_mod_hooks (Fcar (hooks
), make_number (start
),
2210 hooks
= Fcdr (hooks
);
2217 /* Run the interval hooks for an insertion on character range START ... END.
2218 verify_interval_modification chose which hooks to run;
2219 this function is called after the insertion happens
2220 so it can indicate the range of inserted text. */
2223 report_interval_modification (Lisp_Object start
, Lisp_Object end
)
2225 if (! NILP (interval_insert_behind_hooks
))
2226 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2227 if (! NILP (interval_insert_in_front_hooks
)
2228 && ! EQ (interval_insert_in_front_hooks
,
2229 interval_insert_behind_hooks
))
2230 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2234 syms_of_textprop (void)
2236 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
2237 doc
: /* Property-list used as default values.
2238 The value of a property in this list is seen as the value for every
2239 character that does not have its own value for that property. */);
2240 Vdefault_text_properties
= Qnil
;
2242 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist
,
2243 doc
: /* Alist of alternative properties for properties without a value.
2244 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2245 If a piece of text has no direct value for a particular property, then
2246 this alist is consulted. If that property appears in the alist, then
2247 the first non-nil value from the associated alternative properties is
2249 Vchar_property_alias_alist
= Qnil
;
2251 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
2252 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2253 This also inhibits the use of the `intangible' text property. */);
2254 Vinhibit_point_motion_hooks
= Qnil
;
2256 DEFVAR_LISP ("text-property-default-nonsticky",
2257 &Vtext_property_default_nonsticky
,
2258 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2259 Each element has the form (PROPERTY . NONSTICKINESS).
2261 If a character in a buffer has PROPERTY, new text inserted adjacent to
2262 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2263 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2264 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2265 /* Text property `syntax-table' should be nonsticky by default. */
2266 Vtext_property_default_nonsticky
2267 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt
), Qnil
);
2269 staticpro (&interval_insert_behind_hooks
);
2270 staticpro (&interval_insert_in_front_hooks
);
2271 interval_insert_behind_hooks
= Qnil
;
2272 interval_insert_in_front_hooks
= Qnil
;
2275 /* Common attributes one might give text */
2277 staticpro (&Qforeground
);
2278 Qforeground
= intern_c_string ("foreground");
2279 staticpro (&Qbackground
);
2280 Qbackground
= intern_c_string ("background");
2282 Qfont
= intern_c_string ("font");
2283 staticpro (&Qstipple
);
2284 Qstipple
= intern_c_string ("stipple");
2285 staticpro (&Qunderline
);
2286 Qunderline
= intern_c_string ("underline");
2287 staticpro (&Qread_only
);
2288 Qread_only
= intern_c_string ("read-only");
2289 staticpro (&Qinvisible
);
2290 Qinvisible
= intern_c_string ("invisible");
2291 staticpro (&Qintangible
);
2292 Qintangible
= intern_c_string ("intangible");
2293 staticpro (&Qcategory
);
2294 Qcategory
= intern_c_string ("category");
2295 staticpro (&Qlocal_map
);
2296 Qlocal_map
= intern_c_string ("local-map");
2297 staticpro (&Qfront_sticky
);
2298 Qfront_sticky
= intern_c_string ("front-sticky");
2299 staticpro (&Qrear_nonsticky
);
2300 Qrear_nonsticky
= intern_c_string ("rear-nonsticky");
2301 staticpro (&Qmouse_face
);
2302 Qmouse_face
= intern_c_string ("mouse-face");
2303 staticpro (&Qminibuffer_prompt
);
2304 Qminibuffer_prompt
= intern_c_string ("minibuffer-prompt");
2306 /* Properties that text might use to specify certain actions */
2308 staticpro (&Qmouse_left
);
2309 Qmouse_left
= intern_c_string ("mouse-left");
2310 staticpro (&Qmouse_entered
);
2311 Qmouse_entered
= intern_c_string ("mouse-entered");
2312 staticpro (&Qpoint_left
);
2313 Qpoint_left
= intern_c_string ("point-left");
2314 staticpro (&Qpoint_entered
);
2315 Qpoint_entered
= intern_c_string ("point-entered");
2317 defsubr (&Stext_properties_at
);
2318 defsubr (&Sget_text_property
);
2319 defsubr (&Sget_char_property
);
2320 defsubr (&Sget_char_property_and_overlay
);
2321 defsubr (&Snext_char_property_change
);
2322 defsubr (&Sprevious_char_property_change
);
2323 defsubr (&Snext_single_char_property_change
);
2324 defsubr (&Sprevious_single_char_property_change
);
2325 defsubr (&Snext_property_change
);
2326 defsubr (&Snext_single_property_change
);
2327 defsubr (&Sprevious_property_change
);
2328 defsubr (&Sprevious_single_property_change
);
2329 defsubr (&Sadd_text_properties
);
2330 defsubr (&Sput_text_property
);
2331 defsubr (&Sset_text_properties
);
2332 defsubr (&Sremove_text_properties
);
2333 defsubr (&Sremove_list_of_text_properties
);
2334 defsubr (&Stext_property_any
);
2335 defsubr (&Stext_property_not_all
);
2336 /* defsubr (&Serase_text_properties); */
2337 /* defsubr (&Scopy_text_properties); */
2340 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2341 (do not change this comment) */