1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2011 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
22 #include "intervals.h"
27 #define NULL (void *)0
30 /* Test for membership, allowing for t (actually any non-cons) to mean the
33 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
36 /* NOTES: previous- and next- property change will have to skip
37 zero-length intervals if they are implemented. This could be done
38 inside next_interval and previous_interval.
40 set_properties needs to deal with the interval property cache.
42 It is assumed that for any interval plist, a property appears
43 only once on the list. Although some code i.e., remove_properties,
44 handles the more general case, the uniqueness of properties is
45 necessary for the system to remain consistent. This requirement
46 is enforced by the subrs installing properties onto the intervals. */
50 static Lisp_Object Qmouse_left
;
51 static Lisp_Object Qmouse_entered
;
52 Lisp_Object Qpoint_left
;
53 Lisp_Object Qpoint_entered
;
54 Lisp_Object Qcategory
;
55 Lisp_Object Qlocal_map
;
57 /* Visual properties text (including strings) may have. */
58 static Lisp_Object Qforeground
, Qbackground
, Qunderline
;
60 static Lisp_Object Qstipple
;
61 Lisp_Object Qinvisible
, Qintangible
, Qmouse_face
;
62 static Lisp_Object Qread_only
;
63 Lisp_Object Qminibuffer_prompt
;
65 /* Sticky properties */
66 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
68 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
69 the o1's cdr. Otherwise, return zero. This is handy for
71 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
73 /* verify_interval_modification saves insertion hooks here
74 to be run later by report_interval_modification. */
75 static Lisp_Object interval_insert_behind_hooks
;
76 static Lisp_Object interval_insert_in_front_hooks
;
78 static void text_read_only (Lisp_Object
) NO_RETURN
;
79 static Lisp_Object
Fprevious_property_change (Lisp_Object
, Lisp_Object
,
83 /* Signal a `text-read-only' error. This function makes it easier
84 to capture that error in GDB by putting a breakpoint on it. */
87 text_read_only (Lisp_Object propval
)
89 if (STRINGP (propval
))
90 xsignal1 (Qtext_read_only
, propval
);
92 xsignal0 (Qtext_read_only
);
97 /* Extract the interval at the position pointed to by BEGIN from
98 OBJECT, a string or buffer. Additionally, check that the positions
99 pointed to by BEGIN and END are within the bounds of OBJECT, and
100 reverse them if *BEGIN is greater than *END. The objects pointed
101 to by BEGIN and END may be integers or markers; if the latter, they
102 are coerced to integers.
104 When OBJECT is a string, we increment *BEGIN and *END
105 to make them origin-one.
107 Note that buffer points don't correspond to interval indices.
108 For example, point-max is 1 greater than the index of the last
109 character. This difference is handled in the caller, which uses
110 the validated points to determine a length, and operates on that.
111 Exceptions are Ftext_properties_at, Fnext_property_change, and
112 Fprevious_property_change which call this function with BEGIN == END.
113 Handle this case specially.
115 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
116 create an interval tree for OBJECT if one doesn't exist, provided
117 the object actually contains text. In the current design, if there
118 is no text, there can be no text properties. */
124 validate_interval_range (Lisp_Object object
, Lisp_Object
*begin
, Lisp_Object
*end
, int force
)
129 CHECK_STRING_OR_BUFFER (object
);
130 CHECK_NUMBER_COERCE_MARKER (*begin
);
131 CHECK_NUMBER_COERCE_MARKER (*end
);
133 /* If we are asked for a point, but from a subr which operates
134 on a range, then return nothing. */
135 if (EQ (*begin
, *end
) && begin
!= end
)
136 return NULL_INTERVAL
;
138 if (XINT (*begin
) > XINT (*end
))
146 if (BUFFERP (object
))
148 register struct buffer
*b
= XBUFFER (object
);
150 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
151 && XINT (*end
) <= BUF_ZV (b
)))
152 args_out_of_range (*begin
, *end
);
153 i
= BUF_INTERVALS (b
);
155 /* If there's no text, there are no properties. */
156 if (BUF_BEGV (b
) == BUF_ZV (b
))
157 return NULL_INTERVAL
;
159 searchpos
= XINT (*begin
);
163 EMACS_INT len
= SCHARS (object
);
165 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
166 && XINT (*end
) <= len
))
167 args_out_of_range (*begin
, *end
);
168 XSETFASTINT (*begin
, XFASTINT (*begin
));
170 XSETFASTINT (*end
, XFASTINT (*end
));
171 i
= STRING_INTERVALS (object
);
174 return NULL_INTERVAL
;
176 searchpos
= XINT (*begin
);
179 if (NULL_INTERVAL_P (i
))
180 return (force
? create_root_interval (object
) : i
);
182 return find_interval (i
, searchpos
);
185 /* Validate LIST as a property list. If LIST is not a list, then
186 make one consisting of (LIST nil). Otherwise, verify that LIST
187 is even numbered and thus suitable as a plist. */
190 validate_plist (Lisp_Object list
)
198 register Lisp_Object tail
;
199 for (i
= 0, tail
= list
; CONSP (tail
); i
++)
205 error ("Odd length text property list");
209 return Fcons (list
, Fcons (Qnil
, Qnil
));
212 /* Return nonzero if interval I has all the properties,
213 with the same values, of list PLIST. */
216 interval_has_all_properties (Lisp_Object plist
, INTERVAL i
)
218 register Lisp_Object tail1
, tail2
, sym1
;
221 /* Go through each element of PLIST. */
222 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
227 /* Go through I's plist, looking for sym1 */
228 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
229 if (EQ (sym1
, XCAR (tail2
)))
231 /* Found the same property on both lists. If the
232 values are unequal, return zero. */
233 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (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 (Lisp_Object plist
, INTERVAL i
)
254 register Lisp_Object tail1
, tail2
, sym
;
256 /* Go through each element of PLIST. */
257 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
261 /* Go through i's plist, looking for tail1 */
262 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
263 if (EQ (sym
, XCAR (tail2
)))
270 /* Return nonzero if the plist of interval I has any of the
271 property names in LIST, regardless of their values. */
274 interval_has_some_properties_list (Lisp_Object list
, INTERVAL i
)
276 register Lisp_Object tail1
, tail2
, sym
;
278 /* Go through each element of LIST. */
279 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
283 /* Go through i's plist, looking for tail1 */
284 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
285 if (EQ (sym
, XCAR (tail2
)))
292 /* Changing the plists of individual intervals. */
294 /* Return the value of PROP in property-list PLIST, or Qunbound if it
297 property_value (Lisp_Object plist
, Lisp_Object prop
)
301 while (PLIST_ELT_P (plist
, value
))
302 if (EQ (XCAR (plist
), prop
))
305 plist
= XCDR (value
);
310 /* Set the properties of INTERVAL to PROPERTIES,
311 and record undo info for the previous values.
312 OBJECT is the string or buffer that INTERVAL belongs to. */
315 set_properties (Lisp_Object properties
, INTERVAL interval
, Lisp_Object object
)
317 Lisp_Object sym
, value
;
319 if (BUFFERP (object
))
321 /* For each property in the old plist which is missing from PROPERTIES,
322 or has a different value in PROPERTIES, make an undo record. */
323 for (sym
= interval
->plist
;
324 PLIST_ELT_P (sym
, value
);
326 if (! EQ (property_value (properties
, XCAR (sym
)),
329 record_property_change (interval
->position
, LENGTH (interval
),
330 XCAR (sym
), XCAR (value
),
334 /* For each new property that has no value at all in the old plist,
335 make an undo record binding it to nil, so it will be removed. */
336 for (sym
= properties
;
337 PLIST_ELT_P (sym
, value
);
339 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
341 record_property_change (interval
->position
, LENGTH (interval
),
347 /* Store new properties. */
348 interval
->plist
= Fcopy_sequence (properties
);
351 /* Add the properties of PLIST to the interval I, or set
352 the value of I's property to the value of the property on PLIST
353 if they are different.
355 OBJECT should be the string or buffer the interval is in.
357 Return nonzero if this changes I (i.e., if any members of PLIST
358 are actually added to I's plist) */
361 add_properties (Lisp_Object plist
, INTERVAL i
, Lisp_Object object
)
363 Lisp_Object tail1
, tail2
, sym1
, val1
;
364 register int changed
= 0;
366 struct gcpro gcpro1
, gcpro2
, gcpro3
;
371 /* No need to protect OBJECT, because we can GC only in the case
372 where it is a buffer, and live buffers are always protected.
373 I and its plist are also protected, via OBJECT. */
374 GCPRO3 (tail1
, sym1
, val1
);
376 /* Go through each element of PLIST. */
377 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
380 val1
= Fcar (XCDR (tail1
));
383 /* Go through I's plist, looking for sym1 */
384 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
385 if (EQ (sym1
, XCAR (tail2
)))
387 /* No need to gcpro, because tail2 protects this
388 and it must be a cons cell (we get an error otherwise). */
389 register Lisp_Object this_cdr
;
391 this_cdr
= XCDR (tail2
);
392 /* Found the property. Now check its value. */
395 /* The properties have the same value on both lists.
396 Continue to the next property. */
397 if (EQ (val1
, Fcar (this_cdr
)))
400 /* Record this change in the buffer, for undo purposes. */
401 if (BUFFERP (object
))
403 record_property_change (i
->position
, LENGTH (i
),
404 sym1
, Fcar (this_cdr
), object
);
407 /* I's property has a different value -- change it */
408 Fsetcar (this_cdr
, val1
);
415 /* Record this change in the buffer, for undo purposes. */
416 if (BUFFERP (object
))
418 record_property_change (i
->position
, LENGTH (i
),
421 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
431 /* For any members of PLIST, or LIST,
432 which are properties of I, remove them from I's plist.
433 (If PLIST is non-nil, use that, otherwise use LIST.)
434 OBJECT is the string or buffer containing I. */
437 remove_properties (Lisp_Object plist
, Lisp_Object list
, INTERVAL i
, Lisp_Object object
)
439 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
440 register int changed
= 0;
442 /* Nonzero means tail1 is a plist, otherwise it is a list. */
445 current_plist
= i
->plist
;
448 tail1
= plist
, use_plist
= 1;
450 tail1
= list
, use_plist
= 0;
452 /* Go through each element of LIST or PLIST. */
453 while (CONSP (tail1
))
457 /* First, remove the symbol if it's at the head of the list */
458 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
460 if (BUFFERP (object
))
461 record_property_change (i
->position
, LENGTH (i
),
462 sym
, XCAR (XCDR (current_plist
)),
465 current_plist
= XCDR (XCDR (current_plist
));
469 /* Go through I's plist, looking for SYM. */
470 tail2
= current_plist
;
471 while (! NILP (tail2
))
473 register Lisp_Object
this;
474 this = XCDR (XCDR (tail2
));
475 if (CONSP (this) && EQ (sym
, XCAR (this)))
477 if (BUFFERP (object
))
478 record_property_change (i
->position
, LENGTH (i
),
479 sym
, XCAR (XCDR (this)), object
);
481 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
487 /* Advance thru TAIL1 one way or the other. */
488 tail1
= XCDR (tail1
);
489 if (use_plist
&& CONSP (tail1
))
490 tail1
= XCDR (tail1
);
494 i
->plist
= current_plist
;
499 /* Remove all properties from interval I. Return non-zero
500 if this changes the interval. */
503 erase_properties (INTERVAL i
)
513 /* Returns the interval of POSITION in OBJECT.
514 POSITION is BEG-based. */
517 interval_of (EMACS_INT position
, Lisp_Object object
)
523 XSETBUFFER (object
, current_buffer
);
524 else if (EQ (object
, Qt
))
525 return NULL_INTERVAL
;
527 CHECK_STRING_OR_BUFFER (object
);
529 if (BUFFERP (object
))
531 register struct buffer
*b
= XBUFFER (object
);
535 i
= BUF_INTERVALS (b
);
540 end
= SCHARS (object
);
541 i
= STRING_INTERVALS (object
);
544 if (!(beg
<= position
&& position
<= end
))
545 args_out_of_range (make_number (position
), make_number (position
));
546 if (beg
== end
|| NULL_INTERVAL_P (i
))
547 return NULL_INTERVAL
;
549 return find_interval (i
, position
);
552 DEFUN ("text-properties-at", Ftext_properties_at
,
553 Stext_properties_at
, 1, 2, 0,
554 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
555 If the optional second argument OBJECT is a buffer (or nil, which means
556 the current buffer), POSITION is a buffer position (integer or marker).
557 If OBJECT is a string, POSITION is a 0-based index into it.
558 If POSITION is at the end of OBJECT, the value is nil. */)
559 (Lisp_Object position
, Lisp_Object object
)
564 XSETBUFFER (object
, current_buffer
);
566 i
= validate_interval_range (object
, &position
, &position
, soft
);
567 if (NULL_INTERVAL_P (i
))
569 /* If POSITION is at the end of the interval,
570 it means it's the end of OBJECT.
571 There are no properties at the very end,
572 since no character follows. */
573 if (XINT (position
) == LENGTH (i
) + i
->position
)
579 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
580 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
581 OBJECT is optional and defaults to the current buffer.
582 If POSITION is at the end of OBJECT, the value is nil. */)
583 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
585 return textget (Ftext_properties_at (position
, object
), prop
);
588 /* Return the value of char's property PROP, in OBJECT at POSITION.
589 OBJECT is optional and defaults to the current buffer.
590 If OVERLAY is non-0, then in the case that the returned property is from
591 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
592 returned in *OVERLAY.
593 If POSITION is at the end of OBJECT, the value is nil.
594 If OBJECT is a buffer, then overlay properties are considered as well as
596 If OBJECT is a window, then that window's buffer is used, but
597 window-specific overlays are considered only if they are associated
600 get_char_property_and_overlay (Lisp_Object position
, register Lisp_Object prop
, Lisp_Object object
, Lisp_Object
*overlay
)
602 struct window
*w
= 0;
604 CHECK_NUMBER_COERCE_MARKER (position
);
607 XSETBUFFER (object
, current_buffer
);
609 if (WINDOWP (object
))
611 w
= XWINDOW (object
);
614 if (BUFFERP (object
))
617 Lisp_Object
*overlay_vec
;
618 struct buffer
*obuf
= current_buffer
;
620 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
621 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
622 xsignal1 (Qargs_out_of_range
, position
);
624 set_buffer_temp (XBUFFER (object
));
626 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
627 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
629 set_buffer_temp (obuf
);
631 /* Now check the overlays in order of decreasing priority. */
632 while (--noverlays
>= 0)
634 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
638 /* Return the overlay we got the property from. */
639 *overlay
= overlay_vec
[noverlays
];
646 /* Indicate that the return value is not from an overlay. */
649 /* Not a buffer, or no appropriate overlay, so fall through to the
651 return Fget_text_property (position
, prop
, object
);
654 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
655 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
656 Both overlay properties and text properties are checked.
657 OBJECT is optional and defaults to the current buffer.
658 If POSITION is at the end of OBJECT, the value is nil.
659 If OBJECT is a buffer, then overlay properties are considered as well as
661 If OBJECT is a window, then that window's buffer is used, but window-specific
662 overlays are considered only if they are associated with OBJECT. */)
663 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
665 return get_char_property_and_overlay (position
, prop
, object
, 0);
668 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
669 Sget_char_property_and_overlay
, 2, 3, 0,
670 doc
: /* Like `get-char-property', but with extra overlay information.
671 The value is a cons cell. Its car is the return value of `get-char-property'
672 with the same arguments--that is, the value of POSITION's property
673 PROP in OBJECT. Its cdr is the overlay in which the property was
674 found, or nil, if it was found as a text property or not found at all.
676 OBJECT is optional and defaults to the current buffer. OBJECT may be
677 a string, a buffer or a window. For strings, the cdr of the return
678 value is always nil, since strings do not have overlays. If OBJECT is
679 a window, then that window's buffer is used, but window-specific
680 overlays are considered only if they are associated with OBJECT. If
681 POSITION is at the end of OBJECT, both car and cdr are nil. */)
682 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
686 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
687 return Fcons (val
, overlay
);
691 DEFUN ("next-char-property-change", Fnext_char_property_change
,
692 Snext_char_property_change
, 1, 2, 0,
693 doc
: /* Return the position of next text property or overlay change.
694 This scans characters forward in the current buffer from POSITION till
695 it finds a change in some text property, or the beginning or end of an
696 overlay, and returns the position of that.
697 If none is found up to (point-max), the function returns (point-max).
699 If the optional second argument LIMIT is non-nil, don't search
700 past position LIMIT; return LIMIT if nothing is found before LIMIT.
701 LIMIT is a no-op if it is greater than (point-max). */)
702 (Lisp_Object position
, Lisp_Object limit
)
706 temp
= Fnext_overlay_change (position
);
709 CHECK_NUMBER_COERCE_MARKER (limit
);
710 if (XINT (limit
) < XINT (temp
))
713 return Fnext_property_change (position
, Qnil
, temp
);
716 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
717 Sprevious_char_property_change
, 1, 2, 0,
718 doc
: /* Return the position of previous text property or overlay change.
719 Scans characters backward in the current buffer from POSITION till it
720 finds a change in some text property, or the beginning or end of an
721 overlay, and returns the position of that.
722 If none is found since (point-min), the function returns (point-min).
724 If the optional second argument LIMIT is non-nil, don't search
725 past position LIMIT; return LIMIT if nothing is found before LIMIT.
726 LIMIT is a no-op if it is less than (point-min). */)
727 (Lisp_Object position
, Lisp_Object limit
)
731 temp
= Fprevious_overlay_change (position
);
734 CHECK_NUMBER_COERCE_MARKER (limit
);
735 if (XINT (limit
) > XINT (temp
))
738 return Fprevious_property_change (position
, Qnil
, temp
);
742 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
743 Snext_single_char_property_change
, 2, 4, 0,
744 doc
: /* Return the position of next text property or overlay change for a specific property.
745 Scans characters forward from POSITION till it finds
746 a change in the PROP property, then returns the position of the change.
747 If the optional third argument OBJECT is a buffer (or nil, which means
748 the current buffer), POSITION is a buffer position (integer or marker).
749 If OBJECT is a string, POSITION is a 0-based index into it.
751 In a string, scan runs to the end of the string.
752 In a buffer, it runs to (point-max), and the value cannot exceed that.
754 The property values are compared with `eq'.
755 If the property is constant all the way to the end of OBJECT, return the
756 last valid position in OBJECT.
757 If the optional fourth argument LIMIT is non-nil, don't search
758 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
759 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
761 if (STRINGP (object
))
763 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
767 position
= make_number (SCHARS (object
));
770 CHECK_NUMBER (limit
);
777 Lisp_Object initial_value
, value
;
778 int count
= SPECPDL_INDEX ();
781 CHECK_BUFFER (object
);
783 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
785 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
786 Fset_buffer (object
);
789 CHECK_NUMBER_COERCE_MARKER (position
);
791 initial_value
= Fget_char_property (position
, prop
, object
);
794 XSETFASTINT (limit
, ZV
);
796 CHECK_NUMBER_COERCE_MARKER (limit
);
798 if (XFASTINT (position
) >= XFASTINT (limit
))
801 if (XFASTINT (position
) > ZV
)
802 XSETFASTINT (position
, ZV
);
807 position
= Fnext_char_property_change (position
, limit
);
808 if (XFASTINT (position
) >= XFASTINT (limit
))
814 value
= Fget_char_property (position
, prop
, object
);
815 if (!EQ (value
, initial_value
))
819 unbind_to (count
, Qnil
);
825 DEFUN ("previous-single-char-property-change",
826 Fprevious_single_char_property_change
,
827 Sprevious_single_char_property_change
, 2, 4, 0,
828 doc
: /* Return the position of previous text property or overlay change for a specific property.
829 Scans characters backward from POSITION till it finds
830 a change in the PROP property, then returns the position of the change.
831 If the optional third argument OBJECT is a buffer (or nil, which means
832 the current buffer), POSITION is a buffer position (integer or marker).
833 If OBJECT is a string, POSITION is a 0-based index into it.
835 In a string, scan runs to the start of the string.
836 In a buffer, it runs to (point-min), and the value cannot be less than that.
838 The property values are compared with `eq'.
839 If the property is constant all the way to the start of OBJECT, return the
840 first valid position in OBJECT.
841 If the optional fourth argument LIMIT is non-nil, don't search
842 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
843 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
845 if (STRINGP (object
))
847 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
851 position
= make_number (0);
854 CHECK_NUMBER (limit
);
861 int count
= SPECPDL_INDEX ();
864 CHECK_BUFFER (object
);
866 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
868 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
869 Fset_buffer (object
);
872 CHECK_NUMBER_COERCE_MARKER (position
);
875 XSETFASTINT (limit
, BEGV
);
877 CHECK_NUMBER_COERCE_MARKER (limit
);
879 if (XFASTINT (position
) <= XFASTINT (limit
))
882 if (XFASTINT (position
) < BEGV
)
883 XSETFASTINT (position
, BEGV
);
887 Lisp_Object initial_value
888 = Fget_char_property (make_number (XFASTINT (position
) - 1),
893 position
= Fprevious_char_property_change (position
, limit
);
895 if (XFASTINT (position
) <= XFASTINT (limit
))
903 = Fget_char_property (make_number (XFASTINT (position
) - 1),
906 if (!EQ (value
, initial_value
))
912 unbind_to (count
, Qnil
);
918 DEFUN ("next-property-change", Fnext_property_change
,
919 Snext_property_change
, 1, 3, 0,
920 doc
: /* Return the position of next property change.
921 Scans characters forward from POSITION in OBJECT till it finds
922 a change in some text property, then returns the position of the change.
923 If the optional second argument OBJECT is a buffer (or nil, which means
924 the current buffer), POSITION is a buffer position (integer or marker).
925 If OBJECT is a string, POSITION is a 0-based index into it.
926 Return nil if the property is constant all the way to the end of OBJECT.
927 If the value is non-nil, it is a position greater than POSITION, never equal.
929 If the optional third argument LIMIT is non-nil, don't search
930 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
931 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
933 register INTERVAL i
, next
;
936 XSETBUFFER (object
, current_buffer
);
938 if (!NILP (limit
) && !EQ (limit
, Qt
))
939 CHECK_NUMBER_COERCE_MARKER (limit
);
941 i
= validate_interval_range (object
, &position
, &position
, soft
);
943 /* If LIMIT is t, return start of next interval--don't
944 bother checking further intervals. */
947 if (NULL_INTERVAL_P (i
))
950 next
= next_interval (i
);
952 if (NULL_INTERVAL_P (next
))
953 XSETFASTINT (position
, (STRINGP (object
)
955 : BUF_ZV (XBUFFER (object
))));
957 XSETFASTINT (position
, next
->position
);
961 if (NULL_INTERVAL_P (i
))
964 next
= next_interval (i
);
966 while (!NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
967 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
968 next
= next_interval (next
);
970 if (NULL_INTERVAL_P (next
)
976 : BUF_ZV (XBUFFER (object
))))))
979 return make_number (next
->position
);
982 DEFUN ("next-single-property-change", Fnext_single_property_change
,
983 Snext_single_property_change
, 2, 4, 0,
984 doc
: /* Return the position of next property change for a specific property.
985 Scans characters forward from POSITION till it finds
986 a change in the PROP property, then returns the position of the change.
987 If the optional third argument OBJECT is a buffer (or nil, which means
988 the current buffer), POSITION is a buffer position (integer or marker).
989 If OBJECT is a string, POSITION is a 0-based index into it.
990 The property values are compared with `eq'.
991 Return nil if the property is constant all the way to the end of OBJECT.
992 If the value is non-nil, it is a position greater than POSITION, never equal.
994 If the optional fourth argument LIMIT is non-nil, don't search
995 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
996 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
998 register INTERVAL i
, next
;
999 register Lisp_Object here_val
;
1002 XSETBUFFER (object
, current_buffer
);
1005 CHECK_NUMBER_COERCE_MARKER (limit
);
1007 i
= validate_interval_range (object
, &position
, &position
, soft
);
1008 if (NULL_INTERVAL_P (i
))
1011 here_val
= textget (i
->plist
, prop
);
1012 next
= next_interval (i
);
1013 while (! NULL_INTERVAL_P (next
)
1014 && EQ (here_val
, textget (next
->plist
, prop
))
1015 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1016 next
= next_interval (next
);
1018 if (NULL_INTERVAL_P (next
)
1020 >= (INTEGERP (limit
)
1024 : BUF_ZV (XBUFFER (object
))))))
1027 return make_number (next
->position
);
1030 DEFUN ("previous-property-change", Fprevious_property_change
,
1031 Sprevious_property_change
, 1, 3, 0,
1032 doc
: /* Return the position of previous property change.
1033 Scans characters backwards from POSITION in OBJECT till it finds
1034 a change in some text property, then returns the position of the change.
1035 If the optional second argument OBJECT is a buffer (or nil, which means
1036 the current buffer), POSITION is a buffer position (integer or marker).
1037 If OBJECT is a string, POSITION is a 0-based index into it.
1038 Return nil if the property is constant all the way to the start of OBJECT.
1039 If the value is non-nil, it is a position less than POSITION, never equal.
1041 If the optional third argument LIMIT is non-nil, don't search
1042 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1043 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
1045 register INTERVAL i
, previous
;
1048 XSETBUFFER (object
, current_buffer
);
1051 CHECK_NUMBER_COERCE_MARKER (limit
);
1053 i
= validate_interval_range (object
, &position
, &position
, soft
);
1054 if (NULL_INTERVAL_P (i
))
1057 /* Start with the interval containing the char before point. */
1058 if (i
->position
== XFASTINT (position
))
1059 i
= previous_interval (i
);
1061 previous
= previous_interval (i
);
1062 while (!NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1064 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1065 previous
= previous_interval (previous
);
1067 if (NULL_INTERVAL_P (previous
)
1068 || (previous
->position
+ LENGTH (previous
)
1069 <= (INTEGERP (limit
)
1071 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1074 return make_number (previous
->position
+ LENGTH (previous
));
1077 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1078 Sprevious_single_property_change
, 2, 4, 0,
1079 doc
: /* Return the position of previous property change for a specific property.
1080 Scans characters backward from POSITION till it finds
1081 a change in the PROP property, then returns the position of the change.
1082 If the optional third argument OBJECT is a buffer (or nil, which means
1083 the current buffer), POSITION is a buffer position (integer or marker).
1084 If OBJECT is a string, POSITION is a 0-based index into it.
1085 The property values are compared with `eq'.
1086 Return nil if the property is constant all the way to the start of OBJECT.
1087 If the value is non-nil, it is a position less than POSITION, never equal.
1089 If the optional fourth argument LIMIT is non-nil, don't search
1090 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1091 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1093 register INTERVAL i
, previous
;
1094 register Lisp_Object here_val
;
1097 XSETBUFFER (object
, current_buffer
);
1100 CHECK_NUMBER_COERCE_MARKER (limit
);
1102 i
= validate_interval_range (object
, &position
, &position
, soft
);
1104 /* Start with the interval containing the char before point. */
1105 if (!NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1106 i
= previous_interval (i
);
1108 if (NULL_INTERVAL_P (i
))
1111 here_val
= textget (i
->plist
, prop
);
1112 previous
= previous_interval (i
);
1113 while (!NULL_INTERVAL_P (previous
)
1114 && EQ (here_val
, textget (previous
->plist
, prop
))
1116 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1117 previous
= previous_interval (previous
);
1119 if (NULL_INTERVAL_P (previous
)
1120 || (previous
->position
+ LENGTH (previous
)
1121 <= (INTEGERP (limit
)
1123 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1126 return make_number (previous
->position
+ LENGTH (previous
));
1129 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1131 DEFUN ("add-text-properties", Fadd_text_properties
,
1132 Sadd_text_properties
, 3, 4, 0,
1133 doc
: /* Add properties to the text from START to END.
1134 The third argument PROPERTIES is a property list
1135 specifying the property values to add. If the optional fourth argument
1136 OBJECT is a buffer (or nil, which means the current buffer),
1137 START and END are buffer positions (integers or markers).
1138 If OBJECT is a string, START and END are 0-based indices into it.
1139 Return t if any property value actually changed, nil otherwise. */)
1140 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1142 register INTERVAL i
, unchanged
;
1143 register EMACS_INT s
, len
;
1144 register int modified
= 0;
1145 struct gcpro gcpro1
;
1147 properties
= validate_plist (properties
);
1148 if (NILP (properties
))
1152 XSETBUFFER (object
, current_buffer
);
1154 i
= validate_interval_range (object
, &start
, &end
, hard
);
1155 if (NULL_INTERVAL_P (i
))
1159 len
= XINT (end
) - s
;
1161 /* No need to protect OBJECT, because we GC only if it's a buffer,
1162 and live buffers are always protected. */
1163 GCPRO1 (properties
);
1165 /* If we're not starting on an interval boundary, we have to
1166 split this interval. */
1167 if (i
->position
!= s
)
1169 /* If this interval already has the properties, we can
1171 if (interval_has_all_properties (properties
, i
))
1173 EMACS_INT got
= (LENGTH (i
) - (s
- i
->position
));
1175 RETURN_UNGCPRO (Qnil
);
1177 i
= next_interval (i
);
1182 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1183 copy_properties (unchanged
, i
);
1187 if (BUFFERP (object
))
1188 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1190 /* We are at the beginning of interval I, with LEN chars to scan. */
1196 if (LENGTH (i
) >= len
)
1198 /* We can UNGCPRO safely here, because there will be just
1199 one more chance to gc, in the next call to add_properties,
1200 and after that we will not need PROPERTIES or OBJECT again. */
1203 if (interval_has_all_properties (properties
, i
))
1205 if (BUFFERP (object
))
1206 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1207 XINT (end
) - XINT (start
));
1209 return modified
? Qt
: Qnil
;
1212 if (LENGTH (i
) == len
)
1214 add_properties (properties
, i
, object
);
1215 if (BUFFERP (object
))
1216 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1217 XINT (end
) - XINT (start
));
1221 /* i doesn't have the properties, and goes past the change limit */
1223 i
= split_interval_left (unchanged
, len
);
1224 copy_properties (unchanged
, i
);
1225 add_properties (properties
, i
, object
);
1226 if (BUFFERP (object
))
1227 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1228 XINT (end
) - XINT (start
));
1233 modified
+= add_properties (properties
, i
, object
);
1234 i
= next_interval (i
);
1238 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1240 DEFUN ("put-text-property", Fput_text_property
,
1241 Sput_text_property
, 4, 5, 0,
1242 doc
: /* Set one property of the text from START to END.
1243 The third and fourth arguments PROPERTY and VALUE
1244 specify the property to add.
1245 If the optional fifth argument OBJECT is a buffer (or nil, which means
1246 the current buffer), START and END are buffer positions (integers or
1247 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1248 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1250 Fadd_text_properties (start
, end
,
1251 Fcons (property
, Fcons (value
, Qnil
)),
1256 DEFUN ("set-text-properties", Fset_text_properties
,
1257 Sset_text_properties
, 3, 4, 0,
1258 doc
: /* Completely replace properties of text from START to END.
1259 The third argument PROPERTIES is the new property list.
1260 If the optional fourth argument OBJECT is a buffer (or nil, which means
1261 the current buffer), START and END are buffer positions (integers or
1262 markers). If OBJECT is a string, START and END are 0-based indices into it.
1263 If PROPERTIES is nil, the effect is to remove all properties from
1264 the designated part of OBJECT. */)
1265 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1267 return set_text_properties (start
, end
, properties
, object
, Qt
);
1271 /* Replace properties of text from START to END with new list of
1272 properties PROPERTIES. OBJECT is the buffer or string containing
1273 the text. OBJECT nil means use the current buffer.
1274 COHERENT_CHANGE_P nil means this is being called as an internal
1275 subroutine, rather than as a change primitive with checking of
1276 read-only, invoking change hooks, etc.. Value is nil if the
1277 function _detected_ that it did not replace any properties, non-nil
1281 set_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
, Lisp_Object coherent_change_p
)
1283 register INTERVAL i
;
1284 Lisp_Object ostart
, oend
;
1289 properties
= validate_plist (properties
);
1292 XSETBUFFER (object
, current_buffer
);
1294 /* If we want no properties for a whole string,
1295 get rid of its intervals. */
1296 if (NILP (properties
) && STRINGP (object
)
1297 && XFASTINT (start
) == 0
1298 && XFASTINT (end
) == SCHARS (object
))
1300 if (! STRING_INTERVALS (object
))
1303 STRING_SET_INTERVALS (object
, NULL_INTERVAL
);
1307 i
= validate_interval_range (object
, &start
, &end
, soft
);
1309 if (NULL_INTERVAL_P (i
))
1311 /* If buffer has no properties, and we want none, return now. */
1312 if (NILP (properties
))
1315 /* Restore the original START and END values
1316 because validate_interval_range increments them for strings. */
1320 i
= validate_interval_range (object
, &start
, &end
, hard
);
1321 /* This can return if start == end. */
1322 if (NULL_INTERVAL_P (i
))
1326 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1327 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1329 set_text_properties_1 (start
, end
, properties
, object
, i
);
1331 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1332 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1333 XINT (end
) - XINT (start
));
1337 /* Replace properties of text from START to END with new list of
1338 properties PROPERTIES. BUFFER is the buffer containing
1339 the text. This does not obey any hooks.
1340 You can provide the interval that START is located in as I,
1341 or pass NULL for I and this function will find it.
1342 START and END can be in any order. */
1345 set_text_properties_1 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object buffer
, INTERVAL i
)
1347 register INTERVAL prev_changed
= NULL_INTERVAL
;
1348 register EMACS_INT s
, len
;
1351 if (XINT (start
) < XINT (end
))
1354 len
= XINT (end
) - s
;
1356 else if (XINT (end
) < XINT (start
))
1359 len
= XINT (start
) - s
;
1365 i
= find_interval (BUF_INTERVALS (XBUFFER (buffer
)), s
);
1367 if (i
->position
!= s
)
1370 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1372 if (LENGTH (i
) > len
)
1374 copy_properties (unchanged
, i
);
1375 i
= split_interval_left (i
, len
);
1376 set_properties (properties
, i
, buffer
);
1380 set_properties (properties
, i
, buffer
);
1382 if (LENGTH (i
) == len
)
1387 i
= next_interval (i
);
1390 /* We are starting at the beginning of an interval I. LEN is positive. */
1396 if (LENGTH (i
) >= len
)
1398 if (LENGTH (i
) > len
)
1399 i
= split_interval_left (i
, len
);
1401 /* We have to call set_properties even if we are going to
1402 merge the intervals, so as to make the undo records
1403 and cause redisplay to happen. */
1404 set_properties (properties
, i
, buffer
);
1405 if (!NULL_INTERVAL_P (prev_changed
))
1406 merge_interval_left (i
);
1412 /* We have to call set_properties even if we are going to
1413 merge the intervals, so as to make the undo records
1414 and cause redisplay to happen. */
1415 set_properties (properties
, i
, buffer
);
1416 if (NULL_INTERVAL_P (prev_changed
))
1419 prev_changed
= i
= merge_interval_left (i
);
1421 i
= next_interval (i
);
1426 DEFUN ("remove-text-properties", Fremove_text_properties
,
1427 Sremove_text_properties
, 3, 4, 0,
1428 doc
: /* Remove some properties from text from START to END.
1429 The third argument PROPERTIES is a property list
1430 whose property names specify the properties to remove.
1431 \(The values stored in PROPERTIES are ignored.)
1432 If the optional fourth argument OBJECT is a buffer (or nil, which means
1433 the current buffer), START and END are buffer positions (integers or
1434 markers). If OBJECT is a string, START and END are 0-based indices into it.
1435 Return t if any property was actually removed, nil otherwise.
1437 Use `set-text-properties' if you want to remove all text properties. */)
1438 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1440 register INTERVAL i
, unchanged
;
1441 register EMACS_INT s
, len
;
1442 register int modified
= 0;
1445 XSETBUFFER (object
, current_buffer
);
1447 i
= validate_interval_range (object
, &start
, &end
, soft
);
1448 if (NULL_INTERVAL_P (i
))
1452 len
= XINT (end
) - s
;
1454 if (i
->position
!= s
)
1456 /* No properties on this first interval -- return if
1457 it covers the entire region. */
1458 if (! interval_has_some_properties (properties
, i
))
1460 EMACS_INT got
= (LENGTH (i
) - (s
- i
->position
));
1464 i
= next_interval (i
);
1466 /* Split away the beginning of this interval; what we don't
1471 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1472 copy_properties (unchanged
, i
);
1476 if (BUFFERP (object
))
1477 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1479 /* We are at the beginning of an interval, with len to scan */
1485 if (LENGTH (i
) >= len
)
1487 if (! interval_has_some_properties (properties
, i
))
1488 return modified
? Qt
: Qnil
;
1490 if (LENGTH (i
) == len
)
1492 remove_properties (properties
, Qnil
, i
, object
);
1493 if (BUFFERP (object
))
1494 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1495 XINT (end
) - XINT (start
));
1499 /* i has the properties, and goes past the change limit */
1501 i
= split_interval_left (i
, len
);
1502 copy_properties (unchanged
, i
);
1503 remove_properties (properties
, Qnil
, i
, object
);
1504 if (BUFFERP (object
))
1505 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1506 XINT (end
) - XINT (start
));
1511 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1512 i
= next_interval (i
);
1516 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1517 Sremove_list_of_text_properties
, 3, 4, 0,
1518 doc
: /* Remove some properties from text from START to END.
1519 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1520 If the optional fourth argument OBJECT is a buffer (or nil, which means
1521 the current buffer), START and END are buffer positions (integers or
1522 markers). If OBJECT is a string, START and END are 0-based indices into it.
1523 Return t if any property was actually removed, nil otherwise. */)
1524 (Lisp_Object start
, Lisp_Object end
, Lisp_Object list_of_properties
, Lisp_Object object
)
1526 register INTERVAL i
, unchanged
;
1527 register EMACS_INT s
, len
;
1528 register int modified
= 0;
1529 Lisp_Object properties
;
1530 properties
= list_of_properties
;
1533 XSETBUFFER (object
, current_buffer
);
1535 i
= validate_interval_range (object
, &start
, &end
, soft
);
1536 if (NULL_INTERVAL_P (i
))
1540 len
= XINT (end
) - s
;
1542 if (i
->position
!= s
)
1544 /* No properties on this first interval -- return if
1545 it covers the entire region. */
1546 if (! interval_has_some_properties_list (properties
, i
))
1548 EMACS_INT got
= (LENGTH (i
) - (s
- i
->position
));
1552 i
= next_interval (i
);
1554 /* Split away the beginning of this interval; what we don't
1559 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1560 copy_properties (unchanged
, i
);
1564 /* We are at the beginning of an interval, with len to scan.
1565 The flag `modified' records if changes have been made.
1566 When object is a buffer, we must call modify_region before changes are
1567 made and signal_after_change when we are done.
1568 We call modify_region before calling remove_properties if modified == 0,
1569 and we call signal_after_change before returning if modified != 0. */
1575 if (LENGTH (i
) >= len
)
1577 if (! interval_has_some_properties_list (properties
, i
))
1581 if (BUFFERP (object
))
1582 signal_after_change (XINT (start
),
1583 XINT (end
) - XINT (start
),
1584 XINT (end
) - XINT (start
));
1590 else if (LENGTH (i
) == len
)
1592 if (!modified
&& BUFFERP (object
))
1593 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1594 remove_properties (Qnil
, properties
, i
, object
);
1595 if (BUFFERP (object
))
1596 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1597 XINT (end
) - XINT (start
));
1601 { /* i has the properties, and goes past the change limit. */
1603 i
= split_interval_left (i
, len
);
1604 copy_properties (unchanged
, i
);
1605 if (!modified
&& BUFFERP (object
))
1606 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1607 remove_properties (Qnil
, properties
, i
, object
);
1608 if (BUFFERP (object
))
1609 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1610 XINT (end
) - XINT (start
));
1614 if (interval_has_some_properties_list (properties
, i
))
1616 if (!modified
&& BUFFERP (object
))
1617 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1618 remove_properties (Qnil
, properties
, i
, object
);
1622 i
= next_interval (i
);
1626 DEFUN ("text-property-any", Ftext_property_any
,
1627 Stext_property_any
, 4, 5, 0,
1628 doc
: /* Check text from START to END for property PROPERTY equalling VALUE.
1629 If so, return the position of the first character whose property PROPERTY
1630 is `eq' to VALUE. Otherwise return nil.
1631 If the optional fifth argument OBJECT is a buffer (or nil, which means
1632 the current buffer), START and END are buffer positions (integers or
1633 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1634 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1636 register INTERVAL i
;
1637 register EMACS_INT e
, pos
;
1640 XSETBUFFER (object
, current_buffer
);
1641 i
= validate_interval_range (object
, &start
, &end
, soft
);
1642 if (NULL_INTERVAL_P (i
))
1643 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1646 while (! NULL_INTERVAL_P (i
))
1648 if (i
->position
>= e
)
1650 if (EQ (textget (i
->plist
, property
), value
))
1653 if (pos
< XINT (start
))
1655 return make_number (pos
);
1657 i
= next_interval (i
);
1662 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1663 Stext_property_not_all
, 4, 5, 0,
1664 doc
: /* Check text from START to END for property PROPERTY not equalling VALUE.
1665 If so, return the position of the first character whose property PROPERTY
1666 is not `eq' to VALUE. Otherwise, return nil.
1667 If the optional fifth argument OBJECT is a buffer (or nil, which means
1668 the current buffer), START and END are buffer positions (integers or
1669 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1670 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1672 register INTERVAL i
;
1673 register EMACS_INT s
, e
;
1676 XSETBUFFER (object
, current_buffer
);
1677 i
= validate_interval_range (object
, &start
, &end
, soft
);
1678 if (NULL_INTERVAL_P (i
))
1679 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1683 while (! NULL_INTERVAL_P (i
))
1685 if (i
->position
>= e
)
1687 if (! EQ (textget (i
->plist
, property
), value
))
1689 if (i
->position
> s
)
1691 return make_number (s
);
1693 i
= next_interval (i
);
1699 /* Return the direction from which the text-property PROP would be
1700 inherited by any new text inserted at POS: 1 if it would be
1701 inherited from the char after POS, -1 if it would be inherited from
1702 the char before POS, and 0 if from neither.
1703 BUFFER can be either a buffer or nil (meaning current buffer). */
1706 text_property_stickiness (Lisp_Object prop
, Lisp_Object pos
, Lisp_Object buffer
)
1708 Lisp_Object prev_pos
, front_sticky
;
1709 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1712 XSETBUFFER (buffer
, current_buffer
);
1714 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1715 /* Consider previous character. */
1717 Lisp_Object rear_non_sticky
;
1719 prev_pos
= make_number (XINT (pos
) - 1);
1720 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1722 if (!NILP (CONSP (rear_non_sticky
)
1723 ? Fmemq (prop
, rear_non_sticky
)
1725 /* PROP is rear-non-sticky. */
1731 /* Consider following character. */
1732 /* This signals an arg-out-of-range error if pos is outside the
1733 buffer's accessible range. */
1734 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1736 if (EQ (front_sticky
, Qt
)
1737 || (CONSP (front_sticky
)
1738 && !NILP (Fmemq (prop
, front_sticky
))))
1739 /* PROP is inherited from after. */
1740 is_front_sticky
= 1;
1742 /* Simple cases, where the properties are consistent. */
1743 if (is_rear_sticky
&& !is_front_sticky
)
1745 else if (!is_rear_sticky
&& is_front_sticky
)
1747 else if (!is_rear_sticky
&& !is_front_sticky
)
1750 /* The stickiness properties are inconsistent, so we have to
1751 disambiguate. Basically, rear-sticky wins, _except_ if the
1752 property that would be inherited has a value of nil, in which case
1753 front-sticky wins. */
1754 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1755 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1762 /* Copying properties between objects. */
1764 /* Add properties from START to END of SRC, starting at POS in DEST.
1765 SRC and DEST may each refer to strings or buffers.
1766 Optional sixth argument PROP causes only that property to be copied.
1767 Properties are copied to DEST as if by `add-text-properties'.
1768 Return t if any property value actually changed, nil otherwise. */
1770 /* Note this can GC when DEST is a buffer. */
1773 copy_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object src
, Lisp_Object pos
, Lisp_Object dest
, Lisp_Object prop
)
1779 EMACS_INT s
, e
, e2
, p
, len
;
1781 struct gcpro gcpro1
, gcpro2
;
1783 i
= validate_interval_range (src
, &start
, &end
, soft
);
1784 if (NULL_INTERVAL_P (i
))
1787 CHECK_NUMBER_COERCE_MARKER (pos
);
1789 Lisp_Object dest_start
, dest_end
;
1792 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1793 /* Apply this to a copy of pos; it will try to increment its arguments,
1794 which we don't want. */
1795 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1806 e2
= i
->position
+ LENGTH (i
);
1813 while (! NILP (plist
))
1815 if (EQ (Fcar (plist
), prop
))
1817 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1820 plist
= Fcdr (Fcdr (plist
));
1824 /* Must defer modifications to the interval tree in case src
1825 and dest refer to the same string or buffer. */
1826 stuff
= Fcons (Fcons (make_number (p
),
1827 Fcons (make_number (p
+ len
),
1828 Fcons (plist
, Qnil
))),
1832 i
= next_interval (i
);
1833 if (NULL_INTERVAL_P (i
))
1840 GCPRO2 (stuff
, dest
);
1842 while (! NILP (stuff
))
1845 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1846 Fcar (Fcdr (Fcdr (res
))), dest
);
1849 stuff
= Fcdr (stuff
);
1854 return modified
? Qt
: Qnil
;
1858 /* Return a list representing the text properties of OBJECT between
1859 START and END. if PROP is non-nil, report only on that property.
1860 Each result list element has the form (S E PLIST), where S and E
1861 are positions in OBJECT and PLIST is a property list containing the
1862 text properties of OBJECT between S and E. Value is nil if OBJECT
1863 doesn't contain text properties between START and END. */
1866 text_property_list (Lisp_Object object
, Lisp_Object start
, Lisp_Object end
, Lisp_Object prop
)
1873 i
= validate_interval_range (object
, &start
, &end
, soft
);
1874 if (!NULL_INTERVAL_P (i
))
1876 EMACS_INT s
= XINT (start
);
1877 EMACS_INT e
= XINT (end
);
1881 EMACS_INT interval_end
, len
;
1884 interval_end
= i
->position
+ LENGTH (i
);
1885 if (interval_end
> e
)
1887 len
= interval_end
- s
;
1892 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1893 if (EQ (XCAR (plist
), prop
))
1895 plist
= Fcons (prop
, Fcons (Fcar (XCDR (plist
)), Qnil
));
1900 result
= Fcons (Fcons (make_number (s
),
1901 Fcons (make_number (s
+ len
),
1902 Fcons (plist
, Qnil
))),
1905 i
= next_interval (i
);
1906 if (NULL_INTERVAL_P (i
))
1916 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1917 (START END PLIST), where START and END are positions and PLIST is a
1918 property list containing the text properties to add. Adjust START
1919 and END positions by DELTA before adding properties. Value is
1920 non-zero if OBJECT was modified. */
1923 add_text_properties_from_list (Lisp_Object object
, Lisp_Object list
, Lisp_Object delta
)
1925 struct gcpro gcpro1
, gcpro2
;
1928 GCPRO2 (list
, object
);
1930 for (; CONSP (list
); list
= XCDR (list
))
1932 Lisp_Object item
, start
, end
, plist
, tem
;
1935 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1936 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1937 plist
= XCAR (XCDR (XCDR (item
)));
1939 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1950 /* Modify end-points of ranges in LIST destructively, and return the
1951 new list. LIST is a list as returned from text_property_list.
1952 Discard properties that begin at or after NEW_END, and limit
1953 end-points to NEW_END. */
1956 extend_property_ranges (Lisp_Object list
, Lisp_Object new_end
)
1958 Lisp_Object prev
= Qnil
, head
= list
;
1959 EMACS_INT max
= XINT (new_end
);
1961 for (; CONSP (list
); prev
= list
, list
= XCDR (list
))
1963 Lisp_Object item
, beg
, end
;
1967 end
= XCAR (XCDR (item
));
1969 if (XINT (beg
) >= max
)
1971 /* The start-point is past the end of the new string.
1972 Discard this property. */
1973 if (EQ (head
, list
))
1976 XSETCDR (prev
, XCDR (list
));
1978 else if (XINT (end
) > max
)
1979 /* The end-point is past the end of the new string. */
1980 XSETCAR (XCDR (item
), new_end
);
1988 /* Call the modification hook functions in LIST, each with START and END. */
1991 call_mod_hooks (Lisp_Object list
, Lisp_Object start
, Lisp_Object end
)
1993 struct gcpro gcpro1
;
1995 while (!NILP (list
))
1997 call2 (Fcar (list
), start
, end
);
2003 /* Check for read-only intervals between character positions START ... END,
2004 in BUF, and signal an error if we find one.
2006 Then check for any modification hooks in the range.
2007 Create a list of all these hooks in lexicographic order,
2008 eliminating consecutive extra copies of the same hook. Then call
2009 those hooks in order, with START and END - 1 as arguments. */
2012 verify_interval_modification (struct buffer
*buf
,
2013 EMACS_INT start
, EMACS_INT end
)
2015 register INTERVAL intervals
= BUF_INTERVALS (buf
);
2016 register INTERVAL i
;
2018 register Lisp_Object prev_mod_hooks
;
2019 Lisp_Object mod_hooks
;
2020 struct gcpro gcpro1
;
2023 prev_mod_hooks
= Qnil
;
2026 interval_insert_behind_hooks
= Qnil
;
2027 interval_insert_in_front_hooks
= Qnil
;
2029 if (NULL_INTERVAL_P (intervals
))
2034 EMACS_INT temp
= start
;
2039 /* For an insert operation, check the two chars around the position. */
2042 INTERVAL prev
= NULL
;
2043 Lisp_Object before
, after
;
2045 /* Set I to the interval containing the char after START,
2046 and PREV to the interval containing the char before START.
2047 Either one may be null. They may be equal. */
2048 i
= find_interval (intervals
, start
);
2050 if (start
== BUF_BEGV (buf
))
2052 else if (i
->position
== start
)
2053 prev
= previous_interval (i
);
2054 else if (i
->position
< start
)
2056 if (start
== BUF_ZV (buf
))
2059 /* If Vinhibit_read_only is set and is not a list, we can
2060 skip the read_only checks. */
2061 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2063 /* If I and PREV differ we need to check for the read-only
2064 property together with its stickiness. If either I or
2065 PREV are 0, this check is all we need.
2066 We have to take special care, since read-only may be
2067 indirectly defined via the category property. */
2070 if (! NULL_INTERVAL_P (i
))
2072 after
= textget (i
->plist
, Qread_only
);
2074 /* If interval I is read-only and read-only is
2075 front-sticky, inhibit insertion.
2076 Check for read-only as well as category. */
2078 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2082 tem
= textget (i
->plist
, Qfront_sticky
);
2083 if (TMEM (Qread_only
, tem
)
2084 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2085 && TMEM (Qcategory
, tem
)))
2086 text_read_only (after
);
2090 if (! NULL_INTERVAL_P (prev
))
2092 before
= textget (prev
->plist
, Qread_only
);
2094 /* If interval PREV is read-only and read-only isn't
2095 rear-nonsticky, inhibit insertion.
2096 Check for read-only as well as category. */
2098 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2102 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2103 if (! TMEM (Qread_only
, tem
)
2104 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2105 || ! TMEM (Qcategory
, tem
)))
2106 text_read_only (before
);
2110 else if (! NULL_INTERVAL_P (i
))
2112 after
= textget (i
->plist
, Qread_only
);
2114 /* If interval I is read-only and read-only is
2115 front-sticky, inhibit insertion.
2116 Check for read-only as well as category. */
2117 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2121 tem
= textget (i
->plist
, Qfront_sticky
);
2122 if (TMEM (Qread_only
, tem
)
2123 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2124 && TMEM (Qcategory
, tem
)))
2125 text_read_only (after
);
2127 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2128 if (! TMEM (Qread_only
, tem
)
2129 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2130 || ! TMEM (Qcategory
, tem
)))
2131 text_read_only (after
);
2136 /* Run both insert hooks (just once if they're the same). */
2137 if (!NULL_INTERVAL_P (prev
))
2138 interval_insert_behind_hooks
2139 = textget (prev
->plist
, Qinsert_behind_hooks
);
2140 if (!NULL_INTERVAL_P (i
))
2141 interval_insert_in_front_hooks
2142 = textget (i
->plist
, Qinsert_in_front_hooks
);
2146 /* Loop over intervals on or next to START...END,
2147 collecting their hooks. */
2149 i
= find_interval (intervals
, start
);
2152 if (! INTERVAL_WRITABLE_P (i
))
2153 text_read_only (textget (i
->plist
, Qread_only
));
2155 if (!inhibit_modification_hooks
)
2157 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2158 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2160 hooks
= Fcons (mod_hooks
, hooks
);
2161 prev_mod_hooks
= mod_hooks
;
2165 i
= next_interval (i
);
2167 /* Keep going thru the interval containing the char before END. */
2168 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
2170 if (!inhibit_modification_hooks
)
2173 hooks
= Fnreverse (hooks
);
2174 while (! EQ (hooks
, Qnil
))
2176 call_mod_hooks (Fcar (hooks
), make_number (start
),
2178 hooks
= Fcdr (hooks
);
2185 /* Run the interval hooks for an insertion on character range START ... END.
2186 verify_interval_modification chose which hooks to run;
2187 this function is called after the insertion happens
2188 so it can indicate the range of inserted text. */
2191 report_interval_modification (Lisp_Object start
, Lisp_Object end
)
2193 if (! NILP (interval_insert_behind_hooks
))
2194 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2195 if (! NILP (interval_insert_in_front_hooks
)
2196 && ! EQ (interval_insert_in_front_hooks
,
2197 interval_insert_behind_hooks
))
2198 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2202 syms_of_textprop (void)
2204 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties
,
2205 doc
: /* Property-list used as default values.
2206 The value of a property in this list is seen as the value for every
2207 character that does not have its own value for that property. */);
2208 Vdefault_text_properties
= Qnil
;
2210 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist
,
2211 doc
: /* Alist of alternative properties for properties without a value.
2212 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2213 If a piece of text has no direct value for a particular property, then
2214 this alist is consulted. If that property appears in the alist, then
2215 the first non-nil value from the associated alternative properties is
2217 Vchar_property_alias_alist
= Qnil
;
2219 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks
,
2220 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2221 This also inhibits the use of the `intangible' text property. */);
2222 Vinhibit_point_motion_hooks
= Qnil
;
2224 DEFVAR_LISP ("text-property-default-nonsticky",
2225 Vtext_property_default_nonsticky
,
2226 doc
: /* Alist of properties vs the corresponding non-stickinesses.
2227 Each element has the form (PROPERTY . NONSTICKINESS).
2229 If a character in a buffer has PROPERTY, new text inserted adjacent to
2230 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2231 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2232 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2233 /* Text property `syntax-table' should be nonsticky by default. */
2234 Vtext_property_default_nonsticky
2235 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt
), Qnil
);
2237 staticpro (&interval_insert_behind_hooks
);
2238 staticpro (&interval_insert_in_front_hooks
);
2239 interval_insert_behind_hooks
= Qnil
;
2240 interval_insert_in_front_hooks
= Qnil
;
2243 /* Common attributes one might give text */
2245 staticpro (&Qforeground
);
2246 Qforeground
= intern_c_string ("foreground");
2247 staticpro (&Qbackground
);
2248 Qbackground
= intern_c_string ("background");
2250 Qfont
= intern_c_string ("font");
2251 staticpro (&Qstipple
);
2252 Qstipple
= intern_c_string ("stipple");
2253 staticpro (&Qunderline
);
2254 Qunderline
= intern_c_string ("underline");
2255 staticpro (&Qread_only
);
2256 Qread_only
= intern_c_string ("read-only");
2257 staticpro (&Qinvisible
);
2258 Qinvisible
= intern_c_string ("invisible");
2259 staticpro (&Qintangible
);
2260 Qintangible
= intern_c_string ("intangible");
2261 staticpro (&Qcategory
);
2262 Qcategory
= intern_c_string ("category");
2263 staticpro (&Qlocal_map
);
2264 Qlocal_map
= intern_c_string ("local-map");
2265 staticpro (&Qfront_sticky
);
2266 Qfront_sticky
= intern_c_string ("front-sticky");
2267 staticpro (&Qrear_nonsticky
);
2268 Qrear_nonsticky
= intern_c_string ("rear-nonsticky");
2269 staticpro (&Qmouse_face
);
2270 Qmouse_face
= intern_c_string ("mouse-face");
2271 staticpro (&Qminibuffer_prompt
);
2272 Qminibuffer_prompt
= intern_c_string ("minibuffer-prompt");
2274 /* Properties that text might use to specify certain actions */
2276 staticpro (&Qmouse_left
);
2277 Qmouse_left
= intern_c_string ("mouse-left");
2278 staticpro (&Qmouse_entered
);
2279 Qmouse_entered
= intern_c_string ("mouse-entered");
2280 staticpro (&Qpoint_left
);
2281 Qpoint_left
= intern_c_string ("point-left");
2282 staticpro (&Qpoint_entered
);
2283 Qpoint_entered
= intern_c_string ("point-entered");
2285 defsubr (&Stext_properties_at
);
2286 defsubr (&Sget_text_property
);
2287 defsubr (&Sget_char_property
);
2288 defsubr (&Sget_char_property_and_overlay
);
2289 defsubr (&Snext_char_property_change
);
2290 defsubr (&Sprevious_char_property_change
);
2291 defsubr (&Snext_single_char_property_change
);
2292 defsubr (&Sprevious_single_char_property_change
);
2293 defsubr (&Snext_property_change
);
2294 defsubr (&Snext_single_property_change
);
2295 defsubr (&Sprevious_property_change
);
2296 defsubr (&Sprevious_single_property_change
);
2297 defsubr (&Sadd_text_properties
);
2298 defsubr (&Sput_text_property
);
2299 defsubr (&Sset_text_properties
);
2300 defsubr (&Sremove_text_properties
);
2301 defsubr (&Sremove_list_of_text_properties
);
2302 defsubr (&Stext_property_any
);
2303 defsubr (&Stext_property_not_all
);