1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2013 Free Software Foundation,
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"
24 #include "character.h"
28 /* Test for membership, allowing for t (actually any non-cons) to mean the
31 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
34 /* NOTES: previous- and next- property change will have to skip
35 zero-length intervals if they are implemented. This could be done
36 inside next_interval and previous_interval.
38 set_properties needs to deal with the interval property cache.
40 It is assumed that for any interval plist, a property appears
41 only once on the list. Although some code i.e., remove_properties,
42 handles the more general case, the uniqueness of properties is
43 necessary for the system to remain consistent. This requirement
44 is enforced by the subrs installing properties onto the intervals. */
48 static Lisp_Object Qmouse_left
;
49 static Lisp_Object Qmouse_entered
;
50 Lisp_Object Qpoint_left
;
51 Lisp_Object Qpoint_entered
;
52 Lisp_Object Qcategory
;
53 Lisp_Object Qlocal_map
;
55 /* Visual properties text (including strings) may have. */
56 static Lisp_Object Qforeground
, Qbackground
, Qunderline
;
58 static Lisp_Object Qstipple
;
59 Lisp_Object Qinvisible
, Qintangible
, Qmouse_face
;
60 static Lisp_Object Qread_only
;
61 Lisp_Object Qminibuffer_prompt
;
63 enum property_set_type
65 TEXT_PROPERTY_REPLACE
,
66 TEXT_PROPERTY_PREPEND
,
70 /* Sticky properties. */
71 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
73 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
74 the o1's cdr. Otherwise, return zero. This is handy for
76 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
78 /* verify_interval_modification saves insertion hooks here
79 to be run later by report_interval_modification. */
80 static Lisp_Object interval_insert_behind_hooks
;
81 static Lisp_Object interval_insert_in_front_hooks
;
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
);
96 /* Prepare to modify the text properties of BUFFER from START to END. */
99 modify_text_properties (Lisp_Object buffer
, Lisp_Object start
, Lisp_Object end
)
101 ptrdiff_t b
= XINT (start
), e
= XINT (end
);
102 struct buffer
*buf
= XBUFFER (buffer
), *old
= current_buffer
;
104 set_buffer_internal (buf
);
106 prepare_to_modify_buffer_1 (b
, e
, NULL
);
108 BUF_COMPUTE_UNCHANGED (buf
, b
- 1, e
);
109 if (MODIFF
<= SAVE_MODIFF
)
110 record_first_change ();
113 bset_point_before_scroll (current_buffer
, Qnil
);
115 set_buffer_internal (old
);
118 /* Complain if object is not string or buffer type. */
121 CHECK_STRING_OR_BUFFER (Lisp_Object x
)
123 CHECK_TYPE (STRINGP (x
) || BUFFERP (x
), Qbuffer_or_string_p
, x
);
126 /* Extract the interval at the position pointed to by BEGIN from
127 OBJECT, a string or buffer. Additionally, check that the positions
128 pointed to by BEGIN and END are within the bounds of OBJECT, and
129 reverse them if *BEGIN is greater than *END. The objects pointed
130 to by BEGIN and END may be integers or markers; if the latter, they
131 are coerced to integers.
133 When OBJECT is a string, we increment *BEGIN and *END
134 to make them origin-one.
136 Note that buffer points don't correspond to interval indices.
137 For example, point-max is 1 greater than the index of the last
138 character. This difference is handled in the caller, which uses
139 the validated points to determine a length, and operates on that.
140 Exceptions are Ftext_properties_at, Fnext_property_change, and
141 Fprevious_property_change which call this function with BEGIN == END.
142 Handle this case specially.
144 If FORCE is soft (0), it's OK to return NULL. Otherwise,
145 create an interval tree for OBJECT if one doesn't exist, provided
146 the object actually contains text. In the current design, if there
147 is no text, there can be no text properties. */
153 validate_interval_range (Lisp_Object object
, Lisp_Object
*begin
,
154 Lisp_Object
*end
, bool force
)
159 CHECK_STRING_OR_BUFFER (object
);
160 CHECK_NUMBER_COERCE_MARKER (*begin
);
161 CHECK_NUMBER_COERCE_MARKER (*end
);
163 /* If we are asked for a point, but from a subr which operates
164 on a range, then return nothing. */
165 if (EQ (*begin
, *end
) && begin
!= end
)
168 if (XINT (*begin
) > XINT (*end
))
176 if (BUFFERP (object
))
178 register struct buffer
*b
= XBUFFER (object
);
180 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
181 && XINT (*end
) <= BUF_ZV (b
)))
182 args_out_of_range (*begin
, *end
);
183 i
= buffer_intervals (b
);
185 /* If there's no text, there are no properties. */
186 if (BUF_BEGV (b
) == BUF_ZV (b
))
189 searchpos
= XINT (*begin
);
193 ptrdiff_t len
= SCHARS (object
);
195 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
196 && XINT (*end
) <= len
))
197 args_out_of_range (*begin
, *end
);
198 XSETFASTINT (*begin
, XFASTINT (*begin
));
200 XSETFASTINT (*end
, XFASTINT (*end
));
201 i
= string_intervals (object
);
206 searchpos
= XINT (*begin
);
210 return (force
? create_root_interval (object
) : i
);
212 return find_interval (i
, searchpos
);
215 /* Validate LIST as a property list. If LIST is not a list, then
216 make one consisting of (LIST nil). Otherwise, verify that LIST
217 is even numbered and thus suitable as a plist. */
220 validate_plist (Lisp_Object list
)
229 for (tail
= list
; CONSP (tail
); tail
= XCDR (tail
))
235 error ("Odd length text property list");
239 return list2 (list
, Qnil
);
242 /* Return true if interval I has all the properties,
243 with the same values, of list PLIST. */
246 interval_has_all_properties (Lisp_Object plist
, INTERVAL i
)
248 Lisp_Object tail1
, tail2
;
250 /* Go through each element of PLIST. */
251 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
253 Lisp_Object sym1
= XCAR (tail1
);
256 /* Go through I's plist, looking for sym1 */
257 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
258 if (EQ (sym1
, XCAR (tail2
)))
260 /* Found the same property on both lists. If the
261 values are unequal, return zero. */
262 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
265 /* Property has same value on both lists; go to next one. */
277 /* Return true if the plist of interval I has any of the
278 properties of PLIST, regardless of their values. */
281 interval_has_some_properties (Lisp_Object plist
, INTERVAL i
)
283 Lisp_Object tail1
, tail2
, sym
;
285 /* Go through each element of PLIST. */
286 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
290 /* Go through i's plist, looking for tail1 */
291 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
292 if (EQ (sym
, XCAR (tail2
)))
299 /* Return nonzero if the plist of interval I has any of the
300 property names in LIST, regardless of their values. */
303 interval_has_some_properties_list (Lisp_Object list
, INTERVAL i
)
305 Lisp_Object tail1
, tail2
, sym
;
307 /* Go through each element of LIST. */
308 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
312 /* Go through i's plist, looking for tail1 */
313 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
314 if (EQ (sym
, XCAR (tail2
)))
321 /* Changing the plists of individual intervals. */
323 /* Return the value of PROP in property-list PLIST, or Qunbound if it
326 property_value (Lisp_Object plist
, Lisp_Object prop
)
330 while (PLIST_ELT_P (plist
, value
))
331 if (EQ (XCAR (plist
), prop
))
334 plist
= XCDR (value
);
339 /* Set the properties of INTERVAL to PROPERTIES,
340 and record undo info for the previous values.
341 OBJECT is the string or buffer that INTERVAL belongs to. */
344 set_properties (Lisp_Object properties
, INTERVAL interval
, Lisp_Object object
)
346 Lisp_Object sym
, value
;
348 if (BUFFERP (object
))
350 /* For each property in the old plist which is missing from PROPERTIES,
351 or has a different value in PROPERTIES, make an undo record. */
352 for (sym
= interval
->plist
;
353 PLIST_ELT_P (sym
, value
);
355 if (! EQ (property_value (properties
, XCAR (sym
)),
358 record_property_change (interval
->position
, LENGTH (interval
),
359 XCAR (sym
), XCAR (value
),
363 /* For each new property that has no value at all in the old plist,
364 make an undo record binding it to nil, so it will be removed. */
365 for (sym
= properties
;
366 PLIST_ELT_P (sym
, value
);
368 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
370 record_property_change (interval
->position
, LENGTH (interval
),
376 /* Store new properties. */
377 set_interval_plist (interval
, Fcopy_sequence (properties
));
380 /* Add the properties of PLIST to the interval I, or set
381 the value of I's property to the value of the property on PLIST
382 if they are different.
384 OBJECT should be the string or buffer the interval is in.
386 Return true if this changes I (i.e., if any members of PLIST
387 are actually added to I's plist) */
390 add_properties (Lisp_Object plist
, INTERVAL i
, Lisp_Object object
,
391 enum property_set_type set_type
)
393 Lisp_Object tail1
, tail2
, sym1
, val1
;
395 struct gcpro gcpro1
, gcpro2
, gcpro3
;
400 /* No need to protect OBJECT, because we can GC only in the case
401 where it is a buffer, and live buffers are always protected.
402 I and its plist are also protected, via OBJECT. */
403 GCPRO3 (tail1
, sym1
, val1
);
405 /* Go through each element of PLIST. */
406 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
410 val1
= Fcar (XCDR (tail1
));
412 /* Go through I's plist, looking for sym1 */
413 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
414 if (EQ (sym1
, XCAR (tail2
)))
416 /* No need to gcpro, because tail2 protects this
417 and it must be a cons cell (we get an error otherwise). */
418 register Lisp_Object this_cdr
;
420 this_cdr
= XCDR (tail2
);
421 /* Found the property. Now check its value. */
424 /* The properties have the same value on both lists.
425 Continue to the next property. */
426 if (EQ (val1
, Fcar (this_cdr
)))
429 /* Record this change in the buffer, for undo purposes. */
430 if (BUFFERP (object
))
432 record_property_change (i
->position
, LENGTH (i
),
433 sym1
, Fcar (this_cdr
), object
);
436 /* I's property has a different value -- change it */
437 if (set_type
== TEXT_PROPERTY_REPLACE
)
438 Fsetcar (this_cdr
, val1
);
440 if (CONSP (Fcar (this_cdr
)) &&
441 /* Special-case anonymous face properties. */
442 (! EQ (sym1
, Qface
) ||
443 NILP (Fkeywordp (Fcar (Fcar (this_cdr
))))))
444 /* The previous value is a list, so prepend (or
445 append) the new value to this list. */
446 if (set_type
== TEXT_PROPERTY_PREPEND
)
447 Fsetcar (this_cdr
, Fcons (val1
, Fcar (this_cdr
)));
449 nconc2 (Fcar (this_cdr
), list1 (val1
));
451 /* The previous value is a single value, so make it
453 if (set_type
== TEXT_PROPERTY_PREPEND
)
454 Fsetcar (this_cdr
, list2 (val1
, Fcar (this_cdr
)));
456 Fsetcar (this_cdr
, list2 (Fcar (this_cdr
), val1
));
465 /* Record this change in the buffer, for undo purposes. */
466 if (BUFFERP (object
))
468 record_property_change (i
->position
, LENGTH (i
),
471 set_interval_plist (i
, Fcons (sym1
, Fcons (val1
, i
->plist
)));
481 /* For any members of PLIST, or LIST,
482 which are properties of I, remove them from I's plist.
483 (If PLIST is non-nil, use that, otherwise use LIST.)
484 OBJECT is the string or buffer containing I. */
487 remove_properties (Lisp_Object plist
, Lisp_Object list
, INTERVAL i
, Lisp_Object object
)
489 Lisp_Object tail1
, tail2
, sym
, current_plist
;
492 /* True means tail1 is a plist, otherwise it is a list. */
495 current_plist
= i
->plist
;
498 tail1
= plist
, use_plist
= 1;
500 tail1
= list
, use_plist
= 0;
502 /* Go through each element of LIST or PLIST. */
503 while (CONSP (tail1
))
507 /* First, remove the symbol if it's at the head of the list */
508 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
510 if (BUFFERP (object
))
511 record_property_change (i
->position
, LENGTH (i
),
512 sym
, XCAR (XCDR (current_plist
)),
515 current_plist
= XCDR (XCDR (current_plist
));
519 /* Go through I's plist, looking for SYM. */
520 tail2
= current_plist
;
521 while (! NILP (tail2
))
523 register Lisp_Object
this;
524 this = XCDR (XCDR (tail2
));
525 if (CONSP (this) && EQ (sym
, XCAR (this)))
527 if (BUFFERP (object
))
528 record_property_change (i
->position
, LENGTH (i
),
529 sym
, XCAR (XCDR (this)), object
);
531 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
537 /* Advance thru TAIL1 one way or the other. */
538 tail1
= XCDR (tail1
);
539 if (use_plist
&& CONSP (tail1
))
540 tail1
= XCDR (tail1
);
544 set_interval_plist (i
, current_plist
);
548 /* Returns the interval of POSITION in OBJECT.
549 POSITION is BEG-based. */
552 interval_of (ptrdiff_t position
, Lisp_Object object
)
558 XSETBUFFER (object
, current_buffer
);
559 else if (EQ (object
, Qt
))
562 CHECK_STRING_OR_BUFFER (object
);
564 if (BUFFERP (object
))
566 register struct buffer
*b
= XBUFFER (object
);
570 i
= buffer_intervals (b
);
575 end
= SCHARS (object
);
576 i
= string_intervals (object
);
579 if (!(beg
<= position
&& position
<= end
))
580 args_out_of_range (make_number (position
), make_number (position
));
581 if (beg
== end
|| !i
)
584 return find_interval (i
, position
);
587 DEFUN ("text-properties-at", Ftext_properties_at
,
588 Stext_properties_at
, 1, 2, 0,
589 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
590 If the optional second argument OBJECT is a buffer (or nil, which means
591 the current buffer), POSITION is a buffer position (integer or marker).
592 If OBJECT is a string, POSITION is a 0-based index into it.
593 If POSITION is at the end of OBJECT, the value is nil. */)
594 (Lisp_Object position
, Lisp_Object object
)
599 XSETBUFFER (object
, current_buffer
);
601 i
= validate_interval_range (object
, &position
, &position
, soft
);
604 /* If POSITION is at the end of the interval,
605 it means it's the end of OBJECT.
606 There are no properties at the very end,
607 since no character follows. */
608 if (XINT (position
) == LENGTH (i
) + i
->position
)
614 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
615 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
616 OBJECT should be a buffer or a string; if omitted or nil, it defaults
617 to the current buffer.
618 If POSITION is at the end of OBJECT, the value is nil. */)
619 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
621 return textget (Ftext_properties_at (position
, object
), prop
);
624 /* Return the value of char's property PROP, in OBJECT at POSITION.
625 OBJECT is optional and defaults to the current buffer.
626 If OVERLAY is non-0, then in the case that the returned property is from
627 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
628 returned in *OVERLAY.
629 If POSITION is at the end of OBJECT, the value is nil.
630 If OBJECT is a buffer, then overlay properties are considered as well as
632 If OBJECT is a window, then that window's buffer is used, but
633 window-specific overlays are considered only if they are associated
636 get_char_property_and_overlay (Lisp_Object position
, register Lisp_Object prop
, Lisp_Object object
, Lisp_Object
*overlay
)
638 struct window
*w
= 0;
640 CHECK_NUMBER_COERCE_MARKER (position
);
643 XSETBUFFER (object
, current_buffer
);
645 if (WINDOWP (object
))
647 CHECK_LIVE_WINDOW (object
);
648 w
= XWINDOW (object
);
649 object
= w
->contents
;
651 if (BUFFERP (object
))
654 Lisp_Object
*overlay_vec
;
655 struct buffer
*obuf
= current_buffer
;
657 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
658 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
659 xsignal1 (Qargs_out_of_range
, position
);
661 set_buffer_temp (XBUFFER (object
));
663 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
664 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
666 set_buffer_temp (obuf
);
668 /* Now check the overlays in order of decreasing priority. */
669 while (--noverlays
>= 0)
671 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
675 /* Return the overlay we got the property from. */
676 *overlay
= overlay_vec
[noverlays
];
683 /* Indicate that the return value is not from an overlay. */
686 /* Not a buffer, or no appropriate overlay, so fall through to the
688 return Fget_text_property (position
, prop
, object
);
691 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
692 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
693 Both overlay properties and text properties are checked.
694 OBJECT is optional and defaults to the current buffer.
695 If POSITION is at the end of OBJECT, the value is nil.
696 If OBJECT is a buffer, then overlay properties are considered as well as
698 If OBJECT is a window, then that window's buffer is used, but window-specific
699 overlays are considered only if they are associated with OBJECT. */)
700 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
702 return get_char_property_and_overlay (position
, prop
, object
, 0);
705 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
706 Sget_char_property_and_overlay
, 2, 3, 0,
707 doc
: /* Like `get-char-property', but with extra overlay information.
708 The value is a cons cell. Its car is the return value of `get-char-property'
709 with the same arguments--that is, the value of POSITION's property
710 PROP in OBJECT. Its cdr is the overlay in which the property was
711 found, or nil, if it was found as a text property or not found at all.
713 OBJECT is optional and defaults to the current buffer. OBJECT may be
714 a string, a buffer or a window. For strings, the cdr of the return
715 value is always nil, since strings do not have overlays. If OBJECT is
716 a window, then that window's buffer is used, but window-specific
717 overlays are considered only if they are associated with OBJECT. If
718 POSITION is at the end of OBJECT, both car and cdr are nil. */)
719 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
723 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
724 return Fcons (val
, overlay
);
728 DEFUN ("next-char-property-change", Fnext_char_property_change
,
729 Snext_char_property_change
, 1, 2, 0,
730 doc
: /* Return the position of next text property or overlay change.
731 This scans characters forward in the current buffer from POSITION till
732 it finds a change in some text property, or the beginning or end of an
733 overlay, and returns the position of that.
734 If none is found up to (point-max), the function returns (point-max).
736 If the optional second argument LIMIT is non-nil, don't search
737 past position LIMIT; return LIMIT if nothing is found before LIMIT.
738 LIMIT is a no-op if it is greater than (point-max). */)
739 (Lisp_Object position
, Lisp_Object limit
)
743 temp
= Fnext_overlay_change (position
);
746 CHECK_NUMBER_COERCE_MARKER (limit
);
747 if (XINT (limit
) < XINT (temp
))
750 return Fnext_property_change (position
, Qnil
, temp
);
753 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
754 Sprevious_char_property_change
, 1, 2, 0,
755 doc
: /* Return the position of previous text property or overlay change.
756 Scans characters backward in the current buffer from POSITION till it
757 finds a change in some text property, or the beginning or end of an
758 overlay, and returns the position of that.
759 If none is found since (point-min), the function returns (point-min).
761 If the optional second argument LIMIT is non-nil, don't search
762 past position LIMIT; return LIMIT if nothing is found before LIMIT.
763 LIMIT is a no-op if it is less than (point-min). */)
764 (Lisp_Object position
, Lisp_Object limit
)
768 temp
= Fprevious_overlay_change (position
);
771 CHECK_NUMBER_COERCE_MARKER (limit
);
772 if (XINT (limit
) > XINT (temp
))
775 return Fprevious_property_change (position
, Qnil
, temp
);
779 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
780 Snext_single_char_property_change
, 2, 4, 0,
781 doc
: /* Return the position of next text property or overlay change for a specific property.
782 Scans characters forward from POSITION till it finds
783 a change in the PROP property, then returns the position of the change.
784 If the optional third argument OBJECT is a buffer (or nil, which means
785 the current buffer), POSITION is a buffer position (integer or marker).
786 If OBJECT is a string, POSITION is a 0-based index into it.
788 In a string, scan runs to the end of the string.
789 In a buffer, it runs to (point-max), and the value cannot exceed that.
791 The property values are compared with `eq'.
792 If the property is constant all the way to the end of OBJECT, return the
793 last valid position in OBJECT.
794 If the optional fourth argument LIMIT is non-nil, don't search
795 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
796 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
798 if (STRINGP (object
))
800 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
804 position
= make_number (SCHARS (object
));
807 CHECK_NUMBER (limit
);
814 Lisp_Object initial_value
, value
;
815 ptrdiff_t count
= SPECPDL_INDEX ();
818 CHECK_BUFFER (object
);
820 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
822 record_unwind_current_buffer ();
823 Fset_buffer (object
);
826 CHECK_NUMBER_COERCE_MARKER (position
);
828 initial_value
= Fget_char_property (position
, prop
, object
);
831 XSETFASTINT (limit
, ZV
);
833 CHECK_NUMBER_COERCE_MARKER (limit
);
835 if (XFASTINT (position
) >= XFASTINT (limit
))
838 if (XFASTINT (position
) > ZV
)
839 XSETFASTINT (position
, ZV
);
844 position
= Fnext_char_property_change (position
, limit
);
845 if (XFASTINT (position
) >= XFASTINT (limit
))
851 value
= Fget_char_property (position
, prop
, object
);
852 if (!EQ (value
, initial_value
))
856 unbind_to (count
, Qnil
);
862 DEFUN ("previous-single-char-property-change",
863 Fprevious_single_char_property_change
,
864 Sprevious_single_char_property_change
, 2, 4, 0,
865 doc
: /* Return the position of previous text property or overlay change for a specific property.
866 Scans characters backward from POSITION till it finds
867 a change in the PROP property, then returns the position of the change.
868 If the optional third argument OBJECT is a buffer (or nil, which means
869 the current buffer), POSITION is a buffer position (integer or marker).
870 If OBJECT is a string, POSITION is a 0-based index into it.
872 In a string, scan runs to the start of the string.
873 In a buffer, it runs to (point-min), and the value cannot be less than that.
875 The property values are compared with `eq'.
876 If the property is constant all the way to the start of OBJECT, return the
877 first valid position in OBJECT.
878 If the optional fourth argument LIMIT is non-nil, don't search back past
879 position LIMIT; return LIMIT if nothing is found before reaching LIMIT. */)
880 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
882 if (STRINGP (object
))
884 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
888 position
= make_number (0);
891 CHECK_NUMBER (limit
);
898 ptrdiff_t count
= SPECPDL_INDEX ();
901 CHECK_BUFFER (object
);
903 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
905 record_unwind_current_buffer ();
906 Fset_buffer (object
);
909 CHECK_NUMBER_COERCE_MARKER (position
);
912 XSETFASTINT (limit
, BEGV
);
914 CHECK_NUMBER_COERCE_MARKER (limit
);
916 if (XFASTINT (position
) <= XFASTINT (limit
))
919 if (XFASTINT (position
) < BEGV
)
920 XSETFASTINT (position
, BEGV
);
924 Lisp_Object initial_value
925 = Fget_char_property (make_number (XFASTINT (position
) - 1),
930 position
= Fprevious_char_property_change (position
, limit
);
932 if (XFASTINT (position
) <= XFASTINT (limit
))
940 = Fget_char_property (make_number (XFASTINT (position
) - 1),
943 if (!EQ (value
, initial_value
))
949 unbind_to (count
, Qnil
);
955 DEFUN ("next-property-change", Fnext_property_change
,
956 Snext_property_change
, 1, 3, 0,
957 doc
: /* Return the position of next property change.
958 Scans characters forward from POSITION in OBJECT till it finds
959 a change in some text property, then returns the position of the change.
960 If the optional second argument OBJECT is a buffer (or nil, which means
961 the current buffer), POSITION is a buffer position (integer or marker).
962 If OBJECT is a string, POSITION is a 0-based index into it.
963 Return nil if the property is constant all the way to the end of OBJECT.
964 If the value is non-nil, it is a position greater than POSITION, never equal.
966 If the optional third argument LIMIT is non-nil, don't search
967 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
968 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
970 register INTERVAL i
, next
;
973 XSETBUFFER (object
, current_buffer
);
975 if (!NILP (limit
) && !EQ (limit
, Qt
))
976 CHECK_NUMBER_COERCE_MARKER (limit
);
978 i
= validate_interval_range (object
, &position
, &position
, soft
);
980 /* If LIMIT is t, return start of next interval--don't
981 bother checking further intervals. */
987 next
= next_interval (i
);
990 XSETFASTINT (position
, (STRINGP (object
)
992 : BUF_ZV (XBUFFER (object
))));
994 XSETFASTINT (position
, next
->position
);
1001 next
= next_interval (i
);
1003 while (next
&& intervals_equal (i
, next
)
1004 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1005 next
= next_interval (next
);
1009 >= (INTEGERP (limit
)
1013 : BUF_ZV (XBUFFER (object
))))))
1016 return make_number (next
->position
);
1019 DEFUN ("next-single-property-change", Fnext_single_property_change
,
1020 Snext_single_property_change
, 2, 4, 0,
1021 doc
: /* Return the position of next property change for a specific property.
1022 Scans characters forward from POSITION till it finds
1023 a change in the PROP property, then returns the position of the change.
1024 If the optional third argument OBJECT is a buffer (or nil, which means
1025 the current buffer), POSITION is a buffer position (integer or marker).
1026 If OBJECT is a string, POSITION is a 0-based index into it.
1027 The property values are compared with `eq'.
1028 Return nil if the property is constant all the way to the end of OBJECT.
1029 If the value is non-nil, it is a position greater than POSITION, never equal.
1031 If the optional fourth argument LIMIT is non-nil, don't search
1032 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1033 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1035 register INTERVAL i
, next
;
1036 register Lisp_Object here_val
;
1039 XSETBUFFER (object
, current_buffer
);
1042 CHECK_NUMBER_COERCE_MARKER (limit
);
1044 i
= validate_interval_range (object
, &position
, &position
, soft
);
1048 here_val
= textget (i
->plist
, prop
);
1049 next
= next_interval (i
);
1051 && EQ (here_val
, textget (next
->plist
, prop
))
1052 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1053 next
= next_interval (next
);
1057 >= (INTEGERP (limit
)
1061 : BUF_ZV (XBUFFER (object
))))))
1064 return make_number (next
->position
);
1067 DEFUN ("previous-property-change", Fprevious_property_change
,
1068 Sprevious_property_change
, 1, 3, 0,
1069 doc
: /* Return the position of previous property change.
1070 Scans characters backwards from POSITION in OBJECT till it finds
1071 a change in some text property, then returns the position of the change.
1072 If the optional second argument OBJECT is a buffer (or nil, which means
1073 the current buffer), POSITION is a buffer position (integer or marker).
1074 If OBJECT is a string, POSITION is a 0-based index into it.
1075 Return nil if the property is constant all the way to the start of OBJECT.
1076 If the value is non-nil, it is a position less than POSITION, never equal.
1078 If the optional third argument LIMIT is non-nil, don't search
1079 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1080 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
1082 register INTERVAL i
, previous
;
1085 XSETBUFFER (object
, current_buffer
);
1088 CHECK_NUMBER_COERCE_MARKER (limit
);
1090 i
= validate_interval_range (object
, &position
, &position
, soft
);
1094 /* Start with the interval containing the char before point. */
1095 if (i
->position
== XFASTINT (position
))
1096 i
= previous_interval (i
);
1098 previous
= previous_interval (i
);
1099 while (previous
&& intervals_equal (previous
, i
)
1101 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1102 previous
= previous_interval (previous
);
1105 || (previous
->position
+ LENGTH (previous
)
1106 <= (INTEGERP (limit
)
1108 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1111 return make_number (previous
->position
+ LENGTH (previous
));
1114 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1115 Sprevious_single_property_change
, 2, 4, 0,
1116 doc
: /* Return the position of previous property change for a specific property.
1117 Scans characters backward from POSITION till it finds
1118 a change in the PROP property, then returns the position of the change.
1119 If the optional third argument OBJECT is a buffer (or nil, which means
1120 the current buffer), POSITION is a buffer position (integer or marker).
1121 If OBJECT is a string, POSITION is a 0-based index into it.
1122 The property values are compared with `eq'.
1123 Return nil if the property is constant all the way to the start of OBJECT.
1124 If the value is non-nil, it is a position less than POSITION, never equal.
1126 If the optional fourth argument LIMIT is non-nil, don't search
1127 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1128 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1130 register INTERVAL i
, previous
;
1131 register Lisp_Object here_val
;
1134 XSETBUFFER (object
, current_buffer
);
1137 CHECK_NUMBER_COERCE_MARKER (limit
);
1139 i
= validate_interval_range (object
, &position
, &position
, soft
);
1141 /* Start with the interval containing the char before point. */
1142 if (i
&& i
->position
== XFASTINT (position
))
1143 i
= previous_interval (i
);
1148 here_val
= textget (i
->plist
, prop
);
1149 previous
= previous_interval (i
);
1151 && EQ (here_val
, textget (previous
->plist
, prop
))
1153 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1154 previous
= previous_interval (previous
);
1157 || (previous
->position
+ LENGTH (previous
)
1158 <= (INTEGERP (limit
)
1160 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1163 return make_number (previous
->position
+ LENGTH (previous
));
1166 /* Used by add-text-properties and add-face-text-property. */
1169 add_text_properties_1 (Lisp_Object start
, Lisp_Object end
,
1170 Lisp_Object properties
, Lisp_Object object
,
1171 enum property_set_type set_type
) {
1172 INTERVAL i
, unchanged
;
1175 struct gcpro gcpro1
;
1176 bool first_time
= 1;
1178 properties
= validate_plist (properties
);
1179 if (NILP (properties
))
1183 XSETBUFFER (object
, current_buffer
);
1186 i
= validate_interval_range (object
, &start
, &end
, hard
);
1191 len
= XINT (end
) - s
;
1193 /* No need to protect OBJECT, because we GC only if it's a buffer,
1194 and live buffers are always protected. */
1195 GCPRO1 (properties
);
1197 /* If this interval already has the properties, we can skip it. */
1198 if (interval_has_all_properties (properties
, i
))
1200 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1205 RETURN_UNGCPRO (Qnil
);
1207 i
= next_interval (i
);
1210 while (interval_has_all_properties (properties
, i
));
1212 else if (i
->position
!= s
)
1214 /* If we're not starting on an interval boundary, we have to
1215 split this interval. */
1217 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1218 copy_properties (unchanged
, i
);
1221 if (BUFFERP (object
) && first_time
)
1223 ptrdiff_t prev_total_length
= TOTAL_LENGTH (i
);
1224 ptrdiff_t prev_pos
= i
->position
;
1226 modify_text_properties (object
, start
, end
);
1227 /* If someone called us recursively as a side effect of
1228 modify_text_properties, and changed the intervals behind our back
1229 (could happen if lock_file, called by prepare_to_modify_buffer,
1230 triggers redisplay, and that calls add-text-properties again
1231 in the same buffer), we cannot continue with I, because its
1232 data changed. So we restart the interval analysis anew. */
1233 if (TOTAL_LENGTH (i
) != prev_total_length
1234 || i
->position
!= prev_pos
)
1241 /* We are at the beginning of interval I, with LEN chars to scan. */
1246 if (LENGTH (i
) >= len
)
1248 /* We can UNGCPRO safely here, because there will be just
1249 one more chance to gc, in the next call to add_properties,
1250 and after that we will not need PROPERTIES or OBJECT again. */
1253 if (interval_has_all_properties (properties
, i
))
1255 if (BUFFERP (object
))
1256 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1257 XINT (end
) - XINT (start
));
1263 if (LENGTH (i
) == len
)
1265 add_properties (properties
, i
, object
, set_type
);
1266 if (BUFFERP (object
))
1267 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1268 XINT (end
) - XINT (start
));
1272 /* i doesn't have the properties, and goes past the change limit */
1274 i
= split_interval_left (unchanged
, len
);
1275 copy_properties (unchanged
, i
);
1276 add_properties (properties
, i
, object
, set_type
);
1277 if (BUFFERP (object
))
1278 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1279 XINT (end
) - XINT (start
));
1284 modified
|= add_properties (properties
, i
, object
, set_type
);
1285 i
= next_interval (i
);
1289 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1291 DEFUN ("add-text-properties", Fadd_text_properties
,
1292 Sadd_text_properties
, 3, 4, 0,
1293 doc
: /* Add properties to the text from START to END.
1294 The third argument PROPERTIES is a property list
1295 specifying the property values to add. If the optional fourth argument
1296 OBJECT is a buffer (or nil, which means the current buffer),
1297 START and END are buffer positions (integers or markers).
1298 If OBJECT is a string, START and END are 0-based indices into it.
1299 Return t if any property value actually changed, nil otherwise. */)
1300 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
,
1303 return add_text_properties_1 (start
, end
, properties
, object
,
1304 TEXT_PROPERTY_REPLACE
);
1307 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1309 DEFUN ("put-text-property", Fput_text_property
,
1310 Sput_text_property
, 4, 5, 0,
1311 doc
: /* Set one property of the text from START to END.
1312 The third and fourth arguments PROPERTY and VALUE
1313 specify the property to add.
1314 If the optional fifth argument OBJECT is a buffer (or nil, which means
1315 the current buffer), START and END are buffer positions (integers or
1316 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1317 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1319 Fadd_text_properties (start
, end
, list2 (property
, value
), object
);
1323 DEFUN ("set-text-properties", Fset_text_properties
,
1324 Sset_text_properties
, 3, 4, 0,
1325 doc
: /* Completely replace properties of text from START to END.
1326 The third argument PROPERTIES is the new property list.
1327 If the optional fourth argument OBJECT is a buffer (or nil, which means
1328 the current buffer), START and END are buffer positions (integers or
1329 markers). If OBJECT is a string, START and END are 0-based indices into it.
1330 If PROPERTIES is nil, the effect is to remove all properties from
1331 the designated part of OBJECT. */)
1332 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1334 return set_text_properties (start
, end
, properties
, object
, Qt
);
1338 DEFUN ("add-face-text-property", Fadd_face_text_property
,
1339 Sadd_face_text_property
, 3, 5, 0,
1340 doc
: /* Add the face property to the text from START to END.
1341 The third argument FACE specifies the face to add.
1342 If any text in the region already has any face properties, this new
1343 face property will be added to the front of the face property list.
1344 If the optional fourth argument APPENDP is non-nil, append to the end
1345 of the face property list instead.
1346 If the optional fifth argument OBJECT is a buffer (or nil, which means
1347 the current buffer), START and END are buffer positions (integers or
1348 markers). If OBJECT is a string, START and END are 0-based indices
1350 (Lisp_Object start
, Lisp_Object end
, Lisp_Object face
,
1351 Lisp_Object appendp
, Lisp_Object object
)
1353 add_text_properties_1 (start
, end
, list2 (Qface
, face
), object
,
1355 ? TEXT_PROPERTY_PREPEND
1356 : TEXT_PROPERTY_APPEND
));
1360 /* Replace properties of text from START to END with new list of
1361 properties PROPERTIES. OBJECT is the buffer or string containing
1362 the text. OBJECT nil means use the current buffer.
1363 COHERENT_CHANGE_P nil means this is being called as an internal
1364 subroutine, rather than as a change primitive with checking of
1365 read-only, invoking change hooks, etc.. Value is nil if the
1366 function _detected_ that it did not replace any properties, non-nil
1370 set_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
,
1371 Lisp_Object object
, Lisp_Object coherent_change_p
)
1373 register INTERVAL i
;
1374 Lisp_Object ostart
, oend
;
1379 properties
= validate_plist (properties
);
1382 XSETBUFFER (object
, current_buffer
);
1384 /* If we want no properties for a whole string,
1385 get rid of its intervals. */
1386 if (NILP (properties
) && STRINGP (object
)
1387 && XFASTINT (start
) == 0
1388 && XFASTINT (end
) == SCHARS (object
))
1390 if (!string_intervals (object
))
1393 set_string_intervals (object
, NULL
);
1397 i
= validate_interval_range (object
, &start
, &end
, soft
);
1401 /* If buffer has no properties, and we want none, return now. */
1402 if (NILP (properties
))
1405 /* Restore the original START and END values
1406 because validate_interval_range increments them for strings. */
1410 i
= validate_interval_range (object
, &start
, &end
, hard
);
1411 /* This can return if start == end. */
1416 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1417 modify_text_properties (object
, start
, end
);
1419 set_text_properties_1 (start
, end
, properties
, object
, i
);
1421 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1422 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1423 XINT (end
) - XINT (start
));
1427 /* Replace properties of text from START to END with new list of
1428 properties PROPERTIES. OBJECT is the buffer or string containing
1429 the text. This does not obey any hooks.
1430 You should provide the interval that START is located in as I.
1431 START and END can be in any order. */
1434 set_text_properties_1 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
, INTERVAL i
)
1436 register INTERVAL prev_changed
= NULL
;
1437 register ptrdiff_t s
, len
;
1440 if (XINT (start
) < XINT (end
))
1443 len
= XINT (end
) - s
;
1445 else if (XINT (end
) < XINT (start
))
1448 len
= XINT (start
) - s
;
1455 if (i
->position
!= s
)
1458 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1460 if (LENGTH (i
) > len
)
1462 copy_properties (unchanged
, i
);
1463 i
= split_interval_left (i
, len
);
1464 set_properties (properties
, i
, object
);
1468 set_properties (properties
, i
, object
);
1470 if (LENGTH (i
) == len
)
1475 i
= next_interval (i
);
1478 /* We are starting at the beginning of an interval I. LEN is positive. */
1483 if (LENGTH (i
) >= len
)
1485 if (LENGTH (i
) > len
)
1486 i
= split_interval_left (i
, len
);
1488 /* We have to call set_properties even if we are going to
1489 merge the intervals, so as to make the undo records
1490 and cause redisplay to happen. */
1491 set_properties (properties
, i
, object
);
1493 merge_interval_left (i
);
1499 /* We have to call set_properties even if we are going to
1500 merge the intervals, so as to make the undo records
1501 and cause redisplay to happen. */
1502 set_properties (properties
, i
, object
);
1506 prev_changed
= i
= merge_interval_left (i
);
1508 i
= next_interval (i
);
1513 DEFUN ("remove-text-properties", Fremove_text_properties
,
1514 Sremove_text_properties
, 3, 4, 0,
1515 doc
: /* Remove some properties from text from START to END.
1516 The third argument PROPERTIES is a property list
1517 whose property names specify the properties to remove.
1518 \(The values stored in PROPERTIES are ignored.)
1519 If the optional fourth argument OBJECT is a buffer (or nil, which means
1520 the current buffer), START and END are buffer positions (integers or
1521 markers). If OBJECT is a string, START and END are 0-based indices into it.
1522 Return t if any property was actually removed, nil otherwise.
1524 Use `set-text-properties' if you want to remove all text properties. */)
1525 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1527 INTERVAL i
, unchanged
;
1530 bool first_time
= 1;
1533 XSETBUFFER (object
, current_buffer
);
1536 i
= validate_interval_range (object
, &start
, &end
, soft
);
1541 len
= XINT (end
) - s
;
1543 /* If there are no properties on this entire interval, return. */
1544 if (! interval_has_some_properties (properties
, i
))
1546 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1553 i
= next_interval (i
);
1556 while (! interval_has_some_properties (properties
, i
));
1558 /* Split away the beginning of this interval; what we don't
1560 else if (i
->position
!= s
)
1563 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1564 copy_properties (unchanged
, i
);
1567 if (BUFFERP (object
) && first_time
)
1569 ptrdiff_t prev_total_length
= TOTAL_LENGTH (i
);
1570 ptrdiff_t prev_pos
= i
->position
;
1572 modify_text_properties (object
, start
, end
);
1573 /* If someone called us recursively as a side effect of
1574 modify_text_properties, and changed the intervals behind our back
1575 (could happen if lock_file, called by prepare_to_modify_buffer,
1576 triggers redisplay, and that calls add-text-properties again
1577 in the same buffer), we cannot continue with I, because its
1578 data changed. So we restart the interval analysis anew. */
1579 if (TOTAL_LENGTH (i
) != prev_total_length
1580 || i
->position
!= prev_pos
)
1587 /* We are at the beginning of an interval, with len to scan */
1592 if (LENGTH (i
) >= len
)
1594 if (! interval_has_some_properties (properties
, i
))
1597 if (BUFFERP (object
))
1598 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1599 XINT (end
) - XINT (start
));
1603 if (LENGTH (i
) == len
)
1605 remove_properties (properties
, Qnil
, i
, object
);
1606 if (BUFFERP (object
))
1607 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1608 XINT (end
) - XINT (start
));
1612 /* i has the properties, and goes past the change limit */
1614 i
= split_interval_left (i
, len
);
1615 copy_properties (unchanged
, i
);
1616 remove_properties (properties
, Qnil
, i
, object
);
1617 if (BUFFERP (object
))
1618 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1619 XINT (end
) - XINT (start
));
1624 modified
|= remove_properties (properties
, Qnil
, i
, object
);
1625 i
= next_interval (i
);
1629 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1630 Sremove_list_of_text_properties
, 3, 4, 0,
1631 doc
: /* Remove some properties from text from START to END.
1632 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1633 If the optional fourth argument OBJECT is a buffer (or nil, which means
1634 the current buffer), START and END are buffer positions (integers or
1635 markers). If OBJECT is a string, START and END are 0-based indices into it.
1636 Return t if any property was actually removed, nil otherwise. */)
1637 (Lisp_Object start
, Lisp_Object end
, Lisp_Object list_of_properties
, Lisp_Object object
)
1639 INTERVAL i
, unchanged
;
1642 Lisp_Object properties
;
1643 properties
= list_of_properties
;
1646 XSETBUFFER (object
, current_buffer
);
1648 i
= validate_interval_range (object
, &start
, &end
, soft
);
1653 len
= XINT (end
) - s
;
1655 /* If there are no properties on the interval, return. */
1656 if (! interval_has_some_properties_list (properties
, i
))
1658 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1665 i
= next_interval (i
);
1668 while (! interval_has_some_properties_list (properties
, i
));
1670 /* Split away the beginning of this interval; what we don't
1672 else if (i
->position
!= s
)
1675 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1676 copy_properties (unchanged
, i
);
1679 /* We are at the beginning of an interval, with len to scan.
1680 The flag `modified' records if changes have been made.
1681 When object is a buffer, we must call modify_text_properties
1682 before changes are made and signal_after_change when we are done.
1683 We call modify_text_properties before calling remove_properties if modified == 0,
1684 and we call signal_after_change before returning if modified != 0. */
1689 if (LENGTH (i
) >= len
)
1691 if (! interval_has_some_properties_list (properties
, i
))
1695 if (BUFFERP (object
))
1696 signal_after_change (XINT (start
),
1697 XINT (end
) - XINT (start
),
1698 XINT (end
) - XINT (start
));
1704 else if (LENGTH (i
) == len
)
1706 if (!modified
&& BUFFERP (object
))
1707 modify_text_properties (object
, start
, end
);
1708 remove_properties (Qnil
, properties
, i
, object
);
1709 if (BUFFERP (object
))
1710 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1711 XINT (end
) - XINT (start
));
1715 { /* i has the properties, and goes past the change limit. */
1717 i
= split_interval_left (i
, len
);
1718 copy_properties (unchanged
, i
);
1719 if (!modified
&& BUFFERP (object
))
1720 modify_text_properties (object
, start
, end
);
1721 remove_properties (Qnil
, properties
, i
, object
);
1722 if (BUFFERP (object
))
1723 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1724 XINT (end
) - XINT (start
));
1728 if (interval_has_some_properties_list (properties
, i
))
1730 if (!modified
&& BUFFERP (object
))
1731 modify_text_properties (object
, start
, end
);
1732 remove_properties (Qnil
, properties
, i
, object
);
1736 i
= next_interval (i
);
1740 DEFUN ("text-property-any", Ftext_property_any
,
1741 Stext_property_any
, 4, 5, 0,
1742 doc
: /* Check text from START to END for property PROPERTY equaling VALUE.
1743 If so, return the position of the first character whose property PROPERTY
1744 is `eq' to VALUE. Otherwise return nil.
1745 If the optional fifth argument OBJECT is a buffer (or nil, which means
1746 the current buffer), START and END are buffer positions (integers or
1747 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1748 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1750 register INTERVAL i
;
1751 register ptrdiff_t e
, pos
;
1754 XSETBUFFER (object
, current_buffer
);
1755 i
= validate_interval_range (object
, &start
, &end
, soft
);
1757 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1762 if (i
->position
>= e
)
1764 if (EQ (textget (i
->plist
, property
), value
))
1767 if (pos
< XINT (start
))
1769 return make_number (pos
);
1771 i
= next_interval (i
);
1776 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1777 Stext_property_not_all
, 4, 5, 0,
1778 doc
: /* Check text from START to END for property PROPERTY not equaling VALUE.
1779 If so, return the position of the first character whose property PROPERTY
1780 is not `eq' to VALUE. Otherwise, return nil.
1781 If the optional fifth argument OBJECT is a buffer (or nil, which means
1782 the current buffer), START and END are buffer positions (integers or
1783 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1784 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1786 register INTERVAL i
;
1787 register ptrdiff_t s
, e
;
1790 XSETBUFFER (object
, current_buffer
);
1791 i
= validate_interval_range (object
, &start
, &end
, soft
);
1793 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1799 if (i
->position
>= e
)
1801 if (! EQ (textget (i
->plist
, property
), value
))
1803 if (i
->position
> s
)
1805 return make_number (s
);
1807 i
= next_interval (i
);
1813 /* Return the direction from which the text-property PROP would be
1814 inherited by any new text inserted at POS: 1 if it would be
1815 inherited from the char after POS, -1 if it would be inherited from
1816 the char before POS, and 0 if from neither.
1817 BUFFER can be either a buffer or nil (meaning current buffer). */
1820 text_property_stickiness (Lisp_Object prop
, Lisp_Object pos
, Lisp_Object buffer
)
1822 bool ignore_previous_character
;
1823 Lisp_Object prev_pos
= make_number (XINT (pos
) - 1);
1824 Lisp_Object front_sticky
;
1825 bool is_rear_sticky
= true, is_front_sticky
= false; /* defaults */
1826 Lisp_Object defalt
= Fassq (prop
, Vtext_property_default_nonsticky
);
1829 XSETBUFFER (buffer
, current_buffer
);
1831 ignore_previous_character
= XINT (pos
) <= BUF_BEGV (XBUFFER (buffer
));
1833 if (ignore_previous_character
|| (CONSP (defalt
) && !NILP (XCDR (defalt
))))
1834 is_rear_sticky
= false;
1837 Lisp_Object rear_non_sticky
1838 = Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1840 if (!NILP (CONSP (rear_non_sticky
)
1841 ? Fmemq (prop
, rear_non_sticky
)
1843 /* PROP is rear-non-sticky. */
1844 is_rear_sticky
= false;
1847 /* Consider following character. */
1848 /* This signals an arg-out-of-range error if pos is outside the
1849 buffer's accessible range. */
1850 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1852 if (EQ (front_sticky
, Qt
)
1853 || (CONSP (front_sticky
)
1854 && !NILP (Fmemq (prop
, front_sticky
))))
1855 /* PROP is inherited from after. */
1856 is_front_sticky
= true;
1858 /* Simple cases, where the properties are consistent. */
1859 if (is_rear_sticky
&& !is_front_sticky
)
1861 else if (!is_rear_sticky
&& is_front_sticky
)
1863 else if (!is_rear_sticky
&& !is_front_sticky
)
1866 /* The stickiness properties are inconsistent, so we have to
1867 disambiguate. Basically, rear-sticky wins, _except_ if the
1868 property that would be inherited has a value of nil, in which case
1869 front-sticky wins. */
1870 if (ignore_previous_character
1871 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1878 /* Copying properties between objects. */
1880 /* Add properties from START to END of SRC, starting at POS in DEST.
1881 SRC and DEST may each refer to strings or buffers.
1882 Optional sixth argument PROP causes only that property to be copied.
1883 Properties are copied to DEST as if by `add-text-properties'.
1884 Return t if any property value actually changed, nil otherwise. */
1886 /* Note this can GC when DEST is a buffer. */
1889 copy_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object src
, Lisp_Object pos
, Lisp_Object dest
, Lisp_Object prop
)
1895 ptrdiff_t s
, e
, e2
, p
, len
;
1897 struct gcpro gcpro1
, gcpro2
;
1899 i
= validate_interval_range (src
, &start
, &end
, soft
);
1903 CHECK_NUMBER_COERCE_MARKER (pos
);
1905 Lisp_Object dest_start
, dest_end
;
1907 e
= XINT (pos
) + (XINT (end
) - XINT (start
));
1908 if (MOST_POSITIVE_FIXNUM
< e
)
1909 args_out_of_range (pos
, end
);
1911 XSETFASTINT (dest_end
, e
);
1912 /* Apply this to a copy of pos; it will try to increment its arguments,
1913 which we don't want. */
1914 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1925 e2
= i
->position
+ LENGTH (i
);
1932 while (! NILP (plist
))
1934 if (EQ (Fcar (plist
), prop
))
1936 plist
= list2 (prop
, Fcar (Fcdr (plist
)));
1939 plist
= Fcdr (Fcdr (plist
));
1943 /* Must defer modifications to the interval tree in case src
1944 and dest refer to the same string or buffer. */
1945 stuff
= Fcons (list3 (make_number (p
), make_number (p
+ len
), plist
),
1949 i
= next_interval (i
);
1957 GCPRO2 (stuff
, dest
);
1959 while (! NILP (stuff
))
1962 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1963 Fcar (Fcdr (Fcdr (res
))), dest
);
1966 stuff
= Fcdr (stuff
);
1971 return modified
? Qt
: Qnil
;
1975 /* Return a list representing the text properties of OBJECT between
1976 START and END. if PROP is non-nil, report only on that property.
1977 Each result list element has the form (S E PLIST), where S and E
1978 are positions in OBJECT and PLIST is a property list containing the
1979 text properties of OBJECT between S and E. Value is nil if OBJECT
1980 doesn't contain text properties between START and END. */
1983 text_property_list (Lisp_Object object
, Lisp_Object start
, Lisp_Object end
, Lisp_Object prop
)
1990 i
= validate_interval_range (object
, &start
, &end
, soft
);
1993 ptrdiff_t s
= XINT (start
);
1994 ptrdiff_t e
= XINT (end
);
1998 ptrdiff_t interval_end
, len
;
2001 interval_end
= i
->position
+ LENGTH (i
);
2002 if (interval_end
> e
)
2004 len
= interval_end
- s
;
2009 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
2010 if (EQ (XCAR (plist
), prop
))
2012 plist
= list2 (prop
, Fcar (XCDR (plist
)));
2017 result
= Fcons (list3 (make_number (s
), make_number (s
+ len
),
2021 i
= next_interval (i
);
2032 /* Add text properties to OBJECT from LIST. LIST is a list of triples
2033 (START END PLIST), where START and END are positions and PLIST is a
2034 property list containing the text properties to add. Adjust START
2035 and END positions by DELTA before adding properties. */
2038 add_text_properties_from_list (Lisp_Object object
, Lisp_Object list
, Lisp_Object delta
)
2040 struct gcpro gcpro1
, gcpro2
;
2042 GCPRO2 (list
, object
);
2044 for (; CONSP (list
); list
= XCDR (list
))
2046 Lisp_Object item
, start
, end
, plist
;
2049 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
2050 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
2051 plist
= XCAR (XCDR (XCDR (item
)));
2053 Fadd_text_properties (start
, end
, plist
, object
);
2061 /* Modify end-points of ranges in LIST destructively, and return the
2062 new list. LIST is a list as returned from text_property_list.
2063 Discard properties that begin at or after NEW_END, and limit
2064 end-points to NEW_END. */
2067 extend_property_ranges (Lisp_Object list
, Lisp_Object new_end
)
2069 Lisp_Object prev
= Qnil
, head
= list
;
2070 ptrdiff_t max
= XINT (new_end
);
2072 for (; CONSP (list
); prev
= list
, list
= XCDR (list
))
2074 Lisp_Object item
, beg
, end
;
2078 end
= XCAR (XCDR (item
));
2080 if (XINT (beg
) >= max
)
2082 /* The start-point is past the end of the new string.
2083 Discard this property. */
2084 if (EQ (head
, list
))
2087 XSETCDR (prev
, XCDR (list
));
2089 else if (XINT (end
) > max
)
2090 /* The end-point is past the end of the new string. */
2091 XSETCAR (XCDR (item
), new_end
);
2099 /* Call the modification hook functions in LIST, each with START and END. */
2102 call_mod_hooks (Lisp_Object list
, Lisp_Object start
, Lisp_Object end
)
2104 struct gcpro gcpro1
;
2106 while (!NILP (list
))
2108 call2 (Fcar (list
), start
, end
);
2114 /* Check for read-only intervals between character positions START ... END,
2115 in BUF, and signal an error if we find one.
2117 Then check for any modification hooks in the range.
2118 Create a list of all these hooks in lexicographic order,
2119 eliminating consecutive extra copies of the same hook. Then call
2120 those hooks in order, with START and END - 1 as arguments. */
2123 verify_interval_modification (struct buffer
*buf
,
2124 ptrdiff_t start
, ptrdiff_t end
)
2126 INTERVAL intervals
= buffer_intervals (buf
);
2129 Lisp_Object prev_mod_hooks
;
2130 Lisp_Object mod_hooks
;
2131 struct gcpro gcpro1
;
2134 prev_mod_hooks
= Qnil
;
2137 interval_insert_behind_hooks
= Qnil
;
2138 interval_insert_in_front_hooks
= Qnil
;
2145 ptrdiff_t temp
= start
;
2150 /* For an insert operation, check the two chars around the position. */
2153 INTERVAL prev
= NULL
;
2154 Lisp_Object before
, after
;
2156 /* Set I to the interval containing the char after START,
2157 and PREV to the interval containing the char before START.
2158 Either one may be null. They may be equal. */
2159 i
= find_interval (intervals
, start
);
2161 if (start
== BUF_BEGV (buf
))
2163 else if (i
->position
== start
)
2164 prev
= previous_interval (i
);
2165 else if (i
->position
< start
)
2167 if (start
== BUF_ZV (buf
))
2170 /* If Vinhibit_read_only is set and is not a list, we can
2171 skip the read_only checks. */
2172 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2174 /* If I and PREV differ we need to check for the read-only
2175 property together with its stickiness. If either I or
2176 PREV are 0, this check is all we need.
2177 We have to take special care, since read-only may be
2178 indirectly defined via the category property. */
2183 after
= textget (i
->plist
, Qread_only
);
2185 /* If interval I is read-only and read-only is
2186 front-sticky, inhibit insertion.
2187 Check for read-only as well as category. */
2189 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2193 tem
= textget (i
->plist
, Qfront_sticky
);
2194 if (TMEM (Qread_only
, tem
)
2195 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2196 && TMEM (Qcategory
, tem
)))
2197 text_read_only (after
);
2203 before
= textget (prev
->plist
, Qread_only
);
2205 /* If interval PREV is read-only and read-only isn't
2206 rear-nonsticky, inhibit insertion.
2207 Check for read-only as well as category. */
2209 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2213 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2214 if (! TMEM (Qread_only
, tem
)
2215 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2216 || ! TMEM (Qcategory
, tem
)))
2217 text_read_only (before
);
2223 after
= textget (i
->plist
, Qread_only
);
2225 /* If interval I is read-only and read-only is
2226 front-sticky, inhibit insertion.
2227 Check for read-only as well as category. */
2228 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2232 tem
= textget (i
->plist
, Qfront_sticky
);
2233 if (TMEM (Qread_only
, tem
)
2234 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2235 && TMEM (Qcategory
, tem
)))
2236 text_read_only (after
);
2238 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2239 if (! TMEM (Qread_only
, tem
)
2240 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2241 || ! TMEM (Qcategory
, tem
)))
2242 text_read_only (after
);
2247 /* Run both insert hooks (just once if they're the same). */
2249 interval_insert_behind_hooks
2250 = textget (prev
->plist
, Qinsert_behind_hooks
);
2252 interval_insert_in_front_hooks
2253 = textget (i
->plist
, Qinsert_in_front_hooks
);
2257 /* Loop over intervals on or next to START...END,
2258 collecting their hooks. */
2260 i
= find_interval (intervals
, start
);
2263 if (! INTERVAL_WRITABLE_P (i
))
2264 text_read_only (textget (i
->plist
, Qread_only
));
2266 if (!inhibit_modification_hooks
)
2268 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2269 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2271 hooks
= Fcons (mod_hooks
, hooks
);
2272 prev_mod_hooks
= mod_hooks
;
2276 i
= next_interval (i
);
2278 /* Keep going thru the interval containing the char before END. */
2279 while (i
&& i
->position
< end
);
2281 if (!inhibit_modification_hooks
)
2284 hooks
= Fnreverse (hooks
);
2285 while (! EQ (hooks
, Qnil
))
2287 call_mod_hooks (Fcar (hooks
), make_number (start
),
2289 hooks
= Fcdr (hooks
);
2296 /* Run the interval hooks for an insertion on character range START ... END.
2297 verify_interval_modification chose which hooks to run;
2298 this function is called after the insertion happens
2299 so it can indicate the range of inserted text. */
2302 report_interval_modification (Lisp_Object start
, Lisp_Object end
)
2304 if (! NILP (interval_insert_behind_hooks
))
2305 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2306 if (! NILP (interval_insert_in_front_hooks
)
2307 && ! EQ (interval_insert_in_front_hooks
,
2308 interval_insert_behind_hooks
))
2309 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2313 syms_of_textprop (void)
2315 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties
,
2316 doc
: /* Property-list used as default values.
2317 The value of a property in this list is seen as the value for every
2318 character that does not have its own value for that property. */);
2319 Vdefault_text_properties
= Qnil
;
2321 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist
,
2322 doc
: /* Alist of alternative properties for properties without a value.
2323 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2324 If a piece of text has no direct value for a particular property, then
2325 this alist is consulted. If that property appears in the alist, then
2326 the first non-nil value from the associated alternative properties is
2328 Vchar_property_alias_alist
= Qnil
;
2330 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks
,
2331 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2332 This also inhibits the use of the `intangible' text property. */);
2333 Vinhibit_point_motion_hooks
= Qnil
;
2335 DEFVAR_LISP ("text-property-default-nonsticky",
2336 Vtext_property_default_nonsticky
,
2337 doc
: /* Alist of properties vs the corresponding non-stickiness.
2338 Each element has the form (PROPERTY . NONSTICKINESS).
2340 If a character in a buffer has PROPERTY, new text inserted adjacent to
2341 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2342 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2343 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2344 /* Text properties `syntax-table'and `display' should be nonsticky
2346 Vtext_property_default_nonsticky
2347 = list2 (Fcons (intern_c_string ("syntax-table"), Qt
),
2348 Fcons (intern_c_string ("display"), Qt
));
2350 staticpro (&interval_insert_behind_hooks
);
2351 staticpro (&interval_insert_in_front_hooks
);
2352 interval_insert_behind_hooks
= Qnil
;
2353 interval_insert_in_front_hooks
= Qnil
;
2356 /* Common attributes one might give text */
2358 DEFSYM (Qforeground
, "foreground");
2359 DEFSYM (Qbackground
, "background");
2360 DEFSYM (Qfont
, "font");
2361 DEFSYM (Qface
, "face");
2362 DEFSYM (Qstipple
, "stipple");
2363 DEFSYM (Qunderline
, "underline");
2364 DEFSYM (Qread_only
, "read-only");
2365 DEFSYM (Qinvisible
, "invisible");
2366 DEFSYM (Qintangible
, "intangible");
2367 DEFSYM (Qcategory
, "category");
2368 DEFSYM (Qlocal_map
, "local-map");
2369 DEFSYM (Qfront_sticky
, "front-sticky");
2370 DEFSYM (Qrear_nonsticky
, "rear-nonsticky");
2371 DEFSYM (Qmouse_face
, "mouse-face");
2372 DEFSYM (Qminibuffer_prompt
, "minibuffer-prompt");
2374 /* Properties that text might use to specify certain actions */
2376 DEFSYM (Qmouse_left
, "mouse-left");
2377 DEFSYM (Qmouse_entered
, "mouse-entered");
2378 DEFSYM (Qpoint_left
, "point-left");
2379 DEFSYM (Qpoint_entered
, "point-entered");
2381 defsubr (&Stext_properties_at
);
2382 defsubr (&Sget_text_property
);
2383 defsubr (&Sget_char_property
);
2384 defsubr (&Sget_char_property_and_overlay
);
2385 defsubr (&Snext_char_property_change
);
2386 defsubr (&Sprevious_char_property_change
);
2387 defsubr (&Snext_single_char_property_change
);
2388 defsubr (&Sprevious_single_char_property_change
);
2389 defsubr (&Snext_property_change
);
2390 defsubr (&Snext_single_property_change
);
2391 defsubr (&Sprevious_property_change
);
2392 defsubr (&Sprevious_single_property_change
);
2393 defsubr (&Sadd_text_properties
);
2394 defsubr (&Sput_text_property
);
2395 defsubr (&Sset_text_properties
);
2396 defsubr (&Sadd_face_text_property
);
2397 defsubr (&Sremove_text_properties
);
2398 defsubr (&Sremove_list_of_text_properties
);
2399 defsubr (&Stext_property_any
);
2400 defsubr (&Stext_property_not_all
);