1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2018 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 (at
10 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 <https://www.gnu.org/licenses/>. */
23 #include "intervals.h"
27 /* Test for membership, allowing for t (actually any non-cons) to mean the
30 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
33 /* NOTES: previous- and next- property change will have to skip
34 zero-length intervals if they are implemented. This could be done
35 inside next_interval and previous_interval.
37 set_properties needs to deal with the interval property cache.
39 It is assumed that for any interval plist, a property appears
40 only once on the list. Although some code i.e., remove_properties,
41 handles the more general case, the uniqueness of properties is
42 necessary for the system to remain consistent. This requirement
43 is enforced by the subrs installing properties onto the intervals. */
47 enum property_set_type
49 TEXT_PROPERTY_REPLACE
,
50 TEXT_PROPERTY_PREPEND
,
54 /* If o1 is a cons whose cdr is a cons, return true and set o2 to
55 the o1's cdr. Otherwise, return false. This is handy for
57 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
59 /* verify_interval_modification saves insertion hooks here
60 to be run later by report_interval_modification. */
61 static Lisp_Object interval_insert_behind_hooks
;
62 static Lisp_Object interval_insert_in_front_hooks
;
65 /* Signal a `text-read-only' error. This function makes it easier
66 to capture that error in GDB by putting a breakpoint on it. */
69 text_read_only (Lisp_Object propval
)
71 if (STRINGP (propval
))
72 xsignal1 (Qtext_read_only
, propval
);
74 xsignal0 (Qtext_read_only
);
77 /* Prepare to modify the text properties of BUFFER from START to END. */
80 modify_text_properties (Lisp_Object buffer
, Lisp_Object start
, Lisp_Object end
)
82 ptrdiff_t b
= XINT (start
), e
= XINT (end
);
83 struct buffer
*buf
= XBUFFER (buffer
), *old
= current_buffer
;
85 set_buffer_internal (buf
);
87 prepare_to_modify_buffer_1 (b
, e
, NULL
);
89 BUF_COMPUTE_UNCHANGED (buf
, b
- 1, e
);
90 if (MODIFF
<= SAVE_MODIFF
)
91 record_first_change ();
94 bset_point_before_scroll (current_buffer
, Qnil
);
96 set_buffer_internal (old
);
99 /* Complain if object is not string or buffer type. */
102 CHECK_STRING_OR_BUFFER (Lisp_Object x
)
104 CHECK_TYPE (STRINGP (x
) || BUFFERP (x
), Qbuffer_or_string_p
, x
);
107 /* Extract the interval at the position pointed to by BEGIN from
108 OBJECT, a string or buffer. Additionally, check that the positions
109 pointed to by BEGIN and END are within the bounds of OBJECT, and
110 reverse them if *BEGIN is greater than *END. The objects pointed
111 to by BEGIN and END may be integers or markers; if the latter, they
112 are coerced to integers.
114 When OBJECT is a string, we increment *BEGIN and *END
115 to make them origin-one.
117 Note that buffer points don't correspond to interval indices.
118 For example, point-max is 1 greater than the index of the last
119 character. This difference is handled in the caller, which uses
120 the validated points to determine a length, and operates on that.
121 Exceptions are Ftext_properties_at, Fnext_property_change, and
122 Fprevious_property_change which call this function with BEGIN == END.
123 Handle this case specially.
125 If FORCE is soft (false), it's OK to return NULL. Otherwise,
126 create an interval tree for OBJECT if one doesn't exist, provided
127 the object actually contains text. In the current design, if there
128 is no text, there can be no text properties. */
130 enum { soft
= false, hard
= true };
133 validate_interval_range (Lisp_Object object
, Lisp_Object
*begin
,
134 Lisp_Object
*end
, bool force
)
139 CHECK_STRING_OR_BUFFER (object
);
140 CHECK_NUMBER_COERCE_MARKER (*begin
);
141 CHECK_NUMBER_COERCE_MARKER (*end
);
143 /* If we are asked for a point, but from a subr which operates
144 on a range, then return nothing. */
145 if (EQ (*begin
, *end
) && begin
!= end
)
148 if (XINT (*begin
) > XINT (*end
))
156 if (BUFFERP (object
))
158 register struct buffer
*b
= XBUFFER (object
);
160 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
161 && XINT (*end
) <= BUF_ZV (b
)))
162 args_out_of_range (*begin
, *end
);
163 i
= buffer_intervals (b
);
165 /* If there's no text, there are no properties. */
166 if (BUF_BEGV (b
) == BUF_ZV (b
))
169 searchpos
= XINT (*begin
);
173 ptrdiff_t len
= SCHARS (object
);
175 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
176 && XINT (*end
) <= len
))
177 args_out_of_range (*begin
, *end
);
178 XSETFASTINT (*begin
, XFASTINT (*begin
));
180 XSETFASTINT (*end
, XFASTINT (*end
));
181 i
= string_intervals (object
);
186 searchpos
= XINT (*begin
);
190 return (force
? create_root_interval (object
) : i
);
192 return find_interval (i
, searchpos
);
195 /* Validate LIST as a property list. If LIST is not a list, then
196 make one consisting of (LIST nil). Otherwise, verify that LIST
197 is even numbered and thus suitable as a plist. */
200 validate_plist (Lisp_Object list
)
207 Lisp_Object tail
= list
;
212 error ("Odd length text property list");
216 while (CONSP (tail
));
221 return list2 (list
, Qnil
);
224 /* Return true if interval I has all the properties,
225 with the same values, of list PLIST. */
228 interval_has_all_properties (Lisp_Object plist
, INTERVAL i
)
230 Lisp_Object tail1
, tail2
;
232 /* Go through each element of PLIST. */
233 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
235 Lisp_Object sym1
= XCAR (tail1
);
238 /* Go through I's plist, looking for sym1 */
239 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
240 if (EQ (sym1
, XCAR (tail2
)))
242 /* Found the same property on both lists. If the
243 values are unequal, return false. */
244 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
247 /* Property has same value on both lists; go to next one. */
259 /* Return true if the plist of interval I has any of the
260 properties of PLIST, regardless of their values. */
263 interval_has_some_properties (Lisp_Object plist
, INTERVAL i
)
265 Lisp_Object tail1
, tail2
, sym
;
267 /* Go through each element of PLIST. */
268 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
272 /* Go through i's plist, looking for tail1 */
273 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
274 if (EQ (sym
, XCAR (tail2
)))
281 /* Return true if the plist of interval I has any of the
282 property names in LIST, regardless of their values. */
285 interval_has_some_properties_list (Lisp_Object list
, INTERVAL i
)
287 Lisp_Object tail1
, tail2
, sym
;
289 /* Go through each element of LIST. */
290 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
294 /* Go through i's plist, looking for tail1 */
295 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
296 if (EQ (sym
, XCAR (tail2
)))
303 /* Changing the plists of individual intervals. */
305 /* Return the value of PROP in property-list PLIST, or Qunbound if it
308 property_value (Lisp_Object plist
, Lisp_Object prop
)
312 while (PLIST_ELT_P (plist
, value
))
313 if (EQ (XCAR (plist
), prop
))
316 plist
= XCDR (value
);
321 /* Set the properties of INTERVAL to PROPERTIES,
322 and record undo info for the previous values.
323 OBJECT is the string or buffer that INTERVAL belongs to. */
326 set_properties (Lisp_Object properties
, INTERVAL interval
, Lisp_Object object
)
328 Lisp_Object sym
, value
;
330 if (BUFFERP (object
))
332 /* For each property in the old plist which is missing from PROPERTIES,
333 or has a different value in PROPERTIES, make an undo record. */
334 for (sym
= interval
->plist
;
335 PLIST_ELT_P (sym
, value
);
337 if (! EQ (property_value (properties
, XCAR (sym
)),
340 record_property_change (interval
->position
, LENGTH (interval
),
341 XCAR (sym
), XCAR (value
),
345 /* For each new property that has no value at all in the old plist,
346 make an undo record binding it to nil, so it will be removed. */
347 for (sym
= properties
;
348 PLIST_ELT_P (sym
, value
);
350 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
352 record_property_change (interval
->position
, LENGTH (interval
),
358 /* Store new properties. */
359 set_interval_plist (interval
, Fcopy_sequence (properties
));
362 /* Add the properties of PLIST to the interval I, or set
363 the value of I's property to the value of the property on PLIST
364 if they are different.
366 OBJECT should be the string or buffer the interval is in.
368 Return true if this changes I (i.e., if any members of PLIST
369 are actually added to I's plist) */
372 add_properties (Lisp_Object plist
, INTERVAL i
, Lisp_Object object
,
373 enum property_set_type set_type
)
375 Lisp_Object tail1
, tail2
, sym1
, val1
;
376 bool changed
= false;
382 /* Go through each element of PLIST. */
383 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
387 val1
= Fcar (XCDR (tail1
));
389 /* Go through I's plist, looking for sym1 */
390 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
391 if (EQ (sym1
, XCAR (tail2
)))
393 Lisp_Object this_cdr
;
395 this_cdr
= XCDR (tail2
);
396 /* Found the property. Now check its value. */
399 /* The properties have the same value on both lists.
400 Continue to the next property. */
401 if (EQ (val1
, Fcar (this_cdr
)))
404 /* Record this change in the buffer, for undo purposes. */
405 if (BUFFERP (object
))
407 record_property_change (i
->position
, LENGTH (i
),
408 sym1
, Fcar (this_cdr
), object
);
411 /* I's property has a different value -- change it */
412 if (set_type
== TEXT_PROPERTY_REPLACE
)
413 Fsetcar (this_cdr
, val1
);
415 if (CONSP (Fcar (this_cdr
)) &&
416 /* Special-case anonymous face properties. */
417 (! EQ (sym1
, Qface
) ||
418 NILP (Fkeywordp (Fcar (Fcar (this_cdr
))))))
419 /* The previous value is a list, so prepend (or
420 append) the new value to this list. */
421 if (set_type
== TEXT_PROPERTY_PREPEND
)
422 Fsetcar (this_cdr
, Fcons (val1
, Fcar (this_cdr
)));
424 nconc2 (Fcar (this_cdr
), list1 (val1
));
426 /* The previous value is a single value, so make it
428 if (set_type
== TEXT_PROPERTY_PREPEND
)
429 Fsetcar (this_cdr
, list2 (val1
, Fcar (this_cdr
)));
431 Fsetcar (this_cdr
, list2 (Fcar (this_cdr
), val1
));
440 /* Record this change in the buffer, for undo purposes. */
441 if (BUFFERP (object
))
443 record_property_change (i
->position
, LENGTH (i
),
446 set_interval_plist (i
, Fcons (sym1
, Fcons (val1
, i
->plist
)));
454 /* For any members of PLIST, or LIST,
455 which are properties of I, remove them from I's plist.
456 (If PLIST is non-nil, use that, otherwise use LIST.)
457 OBJECT is the string or buffer containing I. */
460 remove_properties (Lisp_Object plist
, Lisp_Object list
, INTERVAL i
, Lisp_Object object
)
462 bool changed
= false;
464 /* True means tail1 is a plist, otherwise it is a list. */
465 bool use_plist
= ! NILP (plist
);
466 Lisp_Object tail1
= use_plist
? plist
: list
;
468 Lisp_Object current_plist
= i
->plist
;
470 /* Go through each element of LIST or PLIST. */
471 while (CONSP (tail1
))
473 Lisp_Object sym
= XCAR (tail1
);
475 /* First, remove the symbol if it's at the head of the list */
476 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
478 if (BUFFERP (object
))
479 record_property_change (i
->position
, LENGTH (i
),
480 sym
, XCAR (XCDR (current_plist
)),
483 current_plist
= XCDR (XCDR (current_plist
));
487 /* Go through I's plist, looking for SYM. */
488 Lisp_Object tail2
= current_plist
;
489 while (! NILP (tail2
))
491 Lisp_Object
this = XCDR (XCDR (tail2
));
492 if (CONSP (this) && EQ (sym
, XCAR (this)))
494 if (BUFFERP (object
))
495 record_property_change (i
->position
, LENGTH (i
),
496 sym
, XCAR (XCDR (this)), object
);
498 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
504 /* Advance thru TAIL1 one way or the other. */
505 tail1
= XCDR (tail1
);
506 if (use_plist
&& CONSP (tail1
))
507 tail1
= XCDR (tail1
);
511 set_interval_plist (i
, current_plist
);
515 /* Returns the interval of POSITION in OBJECT.
516 POSITION is BEG-based. */
519 interval_of (ptrdiff_t position
, Lisp_Object object
)
525 XSETBUFFER (object
, current_buffer
);
526 else if (EQ (object
, Qt
))
529 CHECK_STRING_OR_BUFFER (object
);
531 if (BUFFERP (object
))
533 register struct buffer
*b
= XBUFFER (object
);
537 i
= buffer_intervals (b
);
542 end
= SCHARS (object
);
543 i
= string_intervals (object
);
546 if (!(beg
<= position
&& position
<= end
))
547 args_out_of_range (make_number (position
), make_number (position
));
548 if (beg
== end
|| !i
)
551 return find_interval (i
, position
);
554 DEFUN ("text-properties-at", Ftext_properties_at
,
555 Stext_properties_at
, 1, 2, 0,
556 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
557 If the optional second argument OBJECT is a buffer (or nil, which means
558 the current buffer), POSITION is a buffer position (integer or marker).
559 If OBJECT is a string, POSITION is a 0-based index into it.
560 If POSITION is at the end of OBJECT, the value is nil. */)
561 (Lisp_Object position
, Lisp_Object object
)
566 XSETBUFFER (object
, current_buffer
);
568 i
= validate_interval_range (object
, &position
, &position
, soft
);
571 /* If POSITION is at the end of the interval,
572 it means it's the end of OBJECT.
573 There are no properties at the very end,
574 since no character follows. */
575 if (XINT (position
) == LENGTH (i
) + i
->position
)
581 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
582 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
583 OBJECT should be a buffer or a string; if omitted or nil, it defaults
584 to the current buffer.
585 If POSITION is at the end of OBJECT, the value is nil. */)
586 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
588 return textget (Ftext_properties_at (position
, object
), prop
);
591 /* Return the value of char's property PROP, in OBJECT at POSITION.
592 OBJECT is optional and defaults to the current buffer.
593 If OVERLAY is non-0, then in the case that the returned property is from
594 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
595 returned in *OVERLAY.
596 If POSITION is at the end of OBJECT, the value is nil.
597 If OBJECT is a buffer, then overlay properties are considered as well as
599 If OBJECT is a window, then that window's buffer is used, but
600 window-specific overlays are considered only if they are associated
603 get_char_property_and_overlay (Lisp_Object position
, register Lisp_Object prop
, Lisp_Object object
, Lisp_Object
*overlay
)
605 struct window
*w
= 0;
607 CHECK_NUMBER_COERCE_MARKER (position
);
610 XSETBUFFER (object
, current_buffer
);
612 if (WINDOWP (object
))
614 CHECK_LIVE_WINDOW (object
);
615 w
= XWINDOW (object
);
616 object
= w
->contents
;
618 if (BUFFERP (object
))
621 Lisp_Object
*overlay_vec
;
622 struct buffer
*obuf
= current_buffer
;
624 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
625 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
626 xsignal1 (Qargs_out_of_range
, position
);
628 set_buffer_temp (XBUFFER (object
));
631 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, false);
632 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
634 set_buffer_temp (obuf
);
636 /* Now check the overlays in order of decreasing priority. */
637 while (--noverlays
>= 0)
639 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
643 /* Return the overlay we got the property from. */
644 *overlay
= overlay_vec
[noverlays
];
653 /* Indicate that the return value is not from an overlay. */
656 /* Not a buffer, or no appropriate overlay, so fall through to the
658 return Fget_text_property (position
, prop
, object
);
661 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
662 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
663 Both overlay properties and text properties are checked.
664 OBJECT is optional and defaults to the current buffer.
665 If POSITION is at the end of OBJECT, the value is nil.
666 If OBJECT is a buffer, then overlay properties are considered as well as
668 If OBJECT is a window, then that window's buffer is used, but window-specific
669 overlays are considered only if they are associated with OBJECT. */)
670 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
672 return get_char_property_and_overlay (position
, prop
, object
, 0);
675 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
676 Sget_char_property_and_overlay
, 2, 3, 0,
677 doc
: /* Like `get-char-property', but with extra overlay information.
678 The value is a cons cell. Its car is the return value of `get-char-property'
679 with the same arguments--that is, the value of POSITION's property
680 PROP in OBJECT. Its cdr is the overlay in which the property was
681 found, or nil, if it was found as a text property or not found at all.
683 OBJECT is optional and defaults to the current buffer. OBJECT may be
684 a string, a buffer or a window. For strings, the cdr of the return
685 value is always nil, since strings do not have overlays. If OBJECT is
686 a window, then that window's buffer is used, but window-specific
687 overlays are considered only if they are associated with OBJECT. If
688 POSITION is at the end of OBJECT, both car and cdr are nil. */)
689 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
693 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
694 return Fcons (val
, overlay
);
698 DEFUN ("next-char-property-change", Fnext_char_property_change
,
699 Snext_char_property_change
, 1, 2, 0,
700 doc
: /* Return the position of next text property or overlay change.
701 This scans characters forward in the current buffer from POSITION till
702 it finds a change in some text property, or the beginning or end of an
703 overlay, and returns the position of that.
704 If none is found, and LIMIT is nil or omitted, the function
707 If the optional second argument LIMIT is non-nil, the function doesn't
708 search past position LIMIT, and returns LIMIT if nothing is found
709 before LIMIT. LIMIT is a no-op if it is greater than (point-max). */)
710 (Lisp_Object position
, Lisp_Object limit
)
714 temp
= Fnext_overlay_change (position
);
717 CHECK_NUMBER_COERCE_MARKER (limit
);
718 if (XINT (limit
) < XINT (temp
))
721 return Fnext_property_change (position
, Qnil
, temp
);
724 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
725 Sprevious_char_property_change
, 1, 2, 0,
726 doc
: /* Return the position of previous text property or overlay change.
727 Scans characters backward in the current buffer from POSITION till it
728 finds a change in some text property, or the beginning or end of an
729 overlay, and returns the position of that.
730 If none is found, and LIMIT is nil or omitted, the function
733 If the optional second argument LIMIT is non-nil, the function doesn't
734 search before position LIMIT, and returns LIMIT if nothing is found
735 before LIMIT. LIMIT is a no-op if it is less than (point-min). */)
736 (Lisp_Object position
, Lisp_Object limit
)
740 temp
= Fprevious_overlay_change (position
);
743 CHECK_NUMBER_COERCE_MARKER (limit
);
744 if (XINT (limit
) > XINT (temp
))
747 return Fprevious_property_change (position
, Qnil
, temp
);
751 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
752 Snext_single_char_property_change
, 2, 4, 0,
753 doc
: /* Return the position of next text property or overlay change for a specific property.
754 Scans characters forward from POSITION till it finds
755 a change in the PROP property, then returns the position of the change.
756 If the optional third argument OBJECT is a buffer (or nil, which means
757 the current buffer), POSITION is a buffer position (integer or marker).
758 If OBJECT is a string, POSITION is a 0-based index into it.
760 In a string, scan runs to the end of the string, unless LIMIT is non-nil.
761 In a buffer, if LIMIT is nil or omitted, it runs to (point-max), and the
762 value cannot exceed that.
763 If the optional fourth argument LIMIT is non-nil, don't search
764 past position LIMIT; return LIMIT if nothing is found before LIMIT.
766 The property values are compared with `eq'.
767 If the property is constant all the way to the end of OBJECT, return the
768 last valid position in OBJECT. */)
769 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
771 if (STRINGP (object
))
773 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
777 position
= make_number (SCHARS (object
));
780 CHECK_NUMBER (limit
);
787 Lisp_Object initial_value
, value
;
788 ptrdiff_t count
= SPECPDL_INDEX ();
791 CHECK_BUFFER (object
);
793 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
795 record_unwind_current_buffer ();
796 Fset_buffer (object
);
799 CHECK_NUMBER_COERCE_MARKER (position
);
801 initial_value
= Fget_char_property (position
, prop
, object
);
804 XSETFASTINT (limit
, ZV
);
806 CHECK_NUMBER_COERCE_MARKER (limit
);
808 if (XFASTINT (position
) >= XFASTINT (limit
))
811 if (XFASTINT (position
) > ZV
)
812 XSETFASTINT (position
, ZV
);
817 position
= Fnext_char_property_change (position
, limit
);
818 if (XFASTINT (position
) >= XFASTINT (limit
))
824 value
= Fget_char_property (position
, prop
, object
);
825 if (!EQ (value
, initial_value
))
829 unbind_to (count
, Qnil
);
835 DEFUN ("previous-single-char-property-change",
836 Fprevious_single_char_property_change
,
837 Sprevious_single_char_property_change
, 2, 4, 0,
838 doc
: /* Return the position of previous text property or overlay change for a specific property.
839 Scans characters backward from POSITION till it finds
840 a change in the PROP property, then returns the position of the change.
841 If the optional third argument OBJECT is a buffer (or nil, which means
842 the current buffer), POSITION is a buffer position (integer or marker).
843 If OBJECT is a string, POSITION is a 0-based index into it.
845 In a string, scan runs to the start of the string, unless LIMIT is non-nil.
846 In a buffer, if LIMIT is nil or omitted, it runs to (point-min), and the
847 value cannot be less than that.
848 If the optional fourth argument LIMIT is non-nil, don't search back past
849 position LIMIT; return LIMIT if nothing is found before reaching LIMIT.
851 The property values are compared with `eq'.
852 If the property is constant all the way to the start of OBJECT, return the
853 first valid position in OBJECT. */)
854 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
856 if (STRINGP (object
))
858 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
862 position
= make_number (0);
865 CHECK_NUMBER (limit
);
872 ptrdiff_t count
= SPECPDL_INDEX ();
875 CHECK_BUFFER (object
);
877 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
879 record_unwind_current_buffer ();
880 Fset_buffer (object
);
883 CHECK_NUMBER_COERCE_MARKER (position
);
886 XSETFASTINT (limit
, BEGV
);
888 CHECK_NUMBER_COERCE_MARKER (limit
);
890 if (XFASTINT (position
) <= XFASTINT (limit
))
893 if (XFASTINT (position
) < BEGV
)
894 XSETFASTINT (position
, BEGV
);
898 Lisp_Object initial_value
899 = Fget_char_property (make_number (XFASTINT (position
) - 1),
904 position
= Fprevious_char_property_change (position
, limit
);
906 if (XFASTINT (position
) <= XFASTINT (limit
))
914 = Fget_char_property (make_number (XFASTINT (position
) - 1),
917 if (!EQ (value
, initial_value
))
923 unbind_to (count
, Qnil
);
929 DEFUN ("next-property-change", Fnext_property_change
,
930 Snext_property_change
, 1, 3, 0,
931 doc
: /* Return the position of next property change.
932 Scans characters forward from POSITION in OBJECT till it finds
933 a change in some text property, then returns the position of the change.
934 If the optional second argument OBJECT is a buffer (or nil, which means
935 the current buffer), POSITION is a buffer position (integer or marker).
936 If OBJECT is a string, POSITION is a 0-based index into it.
937 Return nil if LIMIT is nil or omitted, and the property is constant all
938 the way to the end of OBJECT; if the value is non-nil, it is a position
939 greater than POSITION, never equal.
941 If the optional third argument LIMIT is non-nil, don't search
942 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
943 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
945 register INTERVAL i
, next
;
948 XSETBUFFER (object
, current_buffer
);
950 if (!NILP (limit
) && !EQ (limit
, Qt
))
951 CHECK_NUMBER_COERCE_MARKER (limit
);
953 i
= validate_interval_range (object
, &position
, &position
, soft
);
955 /* If LIMIT is t, return start of next interval--don't
956 bother checking further intervals. */
962 next
= next_interval (i
);
965 XSETFASTINT (position
, (STRINGP (object
)
967 : BUF_ZV (XBUFFER (object
))));
969 XSETFASTINT (position
, next
->position
);
976 next
= next_interval (i
);
978 while (next
&& intervals_equal (i
, next
)
979 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
980 next
= next_interval (next
);
988 : BUF_ZV (XBUFFER (object
))))))
991 return make_number (next
->position
);
994 DEFUN ("next-single-property-change", Fnext_single_property_change
,
995 Snext_single_property_change
, 2, 4, 0,
996 doc
: /* Return the position of next property change for a specific property.
997 Scans characters forward from POSITION till it finds
998 a change in the PROP property, then returns the position of the change.
999 If the optional third argument OBJECT is a buffer (or nil, which means
1000 the current buffer), POSITION is a buffer position (integer or marker).
1001 If OBJECT is a string, POSITION is a 0-based index into it.
1002 The property values are compared with `eq'.
1003 Return nil if LIMIT is nil or omitted, and the property is constant all
1004 the way to the end of OBJECT; if the value is non-nil, it is a position
1005 greater than POSITION, never equal.
1007 If the optional fourth argument LIMIT is non-nil, don't search
1008 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1009 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1011 register INTERVAL i
, next
;
1012 register Lisp_Object here_val
;
1015 XSETBUFFER (object
, current_buffer
);
1018 CHECK_NUMBER_COERCE_MARKER (limit
);
1020 i
= validate_interval_range (object
, &position
, &position
, soft
);
1024 here_val
= textget (i
->plist
, prop
);
1025 next
= next_interval (i
);
1027 && EQ (here_val
, textget (next
->plist
, prop
))
1028 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1029 next
= next_interval (next
);
1033 >= (INTEGERP (limit
)
1037 : BUF_ZV (XBUFFER (object
))))))
1040 return make_number (next
->position
);
1043 DEFUN ("previous-property-change", Fprevious_property_change
,
1044 Sprevious_property_change
, 1, 3, 0,
1045 doc
: /* Return the position of previous property change.
1046 Scans characters backwards from POSITION in OBJECT till it finds
1047 a change in some text property, then returns the position of the change.
1048 If the optional second argument OBJECT is a buffer (or nil, which means
1049 the current buffer), POSITION is a buffer position (integer or marker).
1050 If OBJECT is a string, POSITION is a 0-based index into it.
1051 Return nil if LIMIT is nil or omitted, and the property is constant all
1052 the way to the start of OBJECT; if the value is non-nil, it is a position
1053 less than POSITION, never equal.
1055 If the optional third argument LIMIT is non-nil, don't search
1056 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1057 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
1059 register INTERVAL i
, previous
;
1062 XSETBUFFER (object
, current_buffer
);
1065 CHECK_NUMBER_COERCE_MARKER (limit
);
1067 i
= validate_interval_range (object
, &position
, &position
, soft
);
1071 /* Start with the interval containing the char before point. */
1072 if (i
->position
== XFASTINT (position
))
1073 i
= previous_interval (i
);
1075 previous
= previous_interval (i
);
1076 while (previous
&& intervals_equal (previous
, i
)
1078 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1079 previous
= previous_interval (previous
);
1082 || (previous
->position
+ LENGTH (previous
)
1083 <= (INTEGERP (limit
)
1085 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1088 return make_number (previous
->position
+ LENGTH (previous
));
1091 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1092 Sprevious_single_property_change
, 2, 4, 0,
1093 doc
: /* Return the position of previous property change for a specific property.
1094 Scans characters backward from POSITION till it finds
1095 a change in the PROP property, then returns the position of the change.
1096 If the optional third argument OBJECT is a buffer (or nil, which means
1097 the current buffer), POSITION is a buffer position (integer or marker).
1098 If OBJECT is a string, POSITION is a 0-based index into it.
1099 The property values are compared with `eq'.
1100 Return nil if LIMIT is nil or omitted, and the property is constant all
1101 the way to the start of OBJECT; if the value is non-nil, it is a position
1102 less than POSITION, never equal.
1104 If the optional fourth argument LIMIT is non-nil, don't search
1105 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1106 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1108 register INTERVAL i
, previous
;
1109 register Lisp_Object here_val
;
1112 XSETBUFFER (object
, current_buffer
);
1115 CHECK_NUMBER_COERCE_MARKER (limit
);
1117 i
= validate_interval_range (object
, &position
, &position
, soft
);
1119 /* Start with the interval containing the char before point. */
1120 if (i
&& i
->position
== XFASTINT (position
))
1121 i
= previous_interval (i
);
1126 here_val
= textget (i
->plist
, prop
);
1127 previous
= previous_interval (i
);
1129 && EQ (here_val
, textget (previous
->plist
, prop
))
1131 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1132 previous
= previous_interval (previous
);
1135 || (previous
->position
+ LENGTH (previous
)
1136 <= (INTEGERP (limit
)
1138 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1141 return make_number (previous
->position
+ LENGTH (previous
));
1144 /* Used by add-text-properties and add-face-text-property. */
1147 add_text_properties_1 (Lisp_Object start
, Lisp_Object end
,
1148 Lisp_Object properties
, Lisp_Object object
,
1149 enum property_set_type set_type
) {
1150 INTERVAL i
, unchanged
;
1152 bool modified
= false;
1153 bool first_time
= true;
1155 properties
= validate_plist (properties
);
1156 if (NILP (properties
))
1160 XSETBUFFER (object
, current_buffer
);
1163 i
= validate_interval_range (object
, &start
, &end
, hard
);
1168 len
= XINT (end
) - s
;
1170 /* If this interval already has the properties, we can skip it. */
1171 if (interval_has_all_properties (properties
, i
))
1173 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1180 i
= next_interval (i
);
1183 while (interval_has_all_properties (properties
, i
));
1185 else if (i
->position
!= s
)
1187 /* If we're not starting on an interval boundary, we have to
1188 split this interval. */
1190 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1191 copy_properties (unchanged
, i
);
1194 if (BUFFERP (object
) && first_time
)
1196 ptrdiff_t prev_total_length
= TOTAL_LENGTH (i
);
1197 ptrdiff_t prev_pos
= i
->position
;
1199 modify_text_properties (object
, start
, end
);
1200 /* If someone called us recursively as a side effect of
1201 modify_text_properties, and changed the intervals behind our back
1202 (could happen if lock_file, called by prepare_to_modify_buffer,
1203 triggers redisplay, and that calls add-text-properties again
1204 in the same buffer), we cannot continue with I, because its
1205 data changed. So we restart the interval analysis anew. */
1206 if (TOTAL_LENGTH (i
) != prev_total_length
1207 || i
->position
!= prev_pos
)
1214 /* We are at the beginning of interval I, with LEN chars to scan. */
1219 if (LENGTH (i
) >= len
)
1221 if (interval_has_all_properties (properties
, i
))
1223 if (BUFFERP (object
))
1224 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1225 XINT (end
) - XINT (start
));
1231 if (LENGTH (i
) == len
)
1233 add_properties (properties
, i
, object
, set_type
);
1234 if (BUFFERP (object
))
1235 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1236 XINT (end
) - XINT (start
));
1240 /* i doesn't have the properties, and goes past the change limit */
1242 i
= split_interval_left (unchanged
, len
);
1243 copy_properties (unchanged
, i
);
1244 add_properties (properties
, i
, object
, set_type
);
1245 if (BUFFERP (object
))
1246 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1247 XINT (end
) - XINT (start
));
1252 modified
|= add_properties (properties
, i
, object
, set_type
);
1253 i
= next_interval (i
);
1257 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1259 DEFUN ("add-text-properties", Fadd_text_properties
,
1260 Sadd_text_properties
, 3, 4, 0,
1261 doc
: /* Add properties to the text from START to END.
1262 The third argument PROPERTIES is a property list
1263 specifying the property values to add. If the optional fourth argument
1264 OBJECT is a buffer (or nil, which means the current buffer),
1265 START and END are buffer positions (integers or markers).
1266 If OBJECT is a string, START and END are 0-based indices into it.
1267 Return t if any property value actually changed, nil otherwise. */)
1268 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
,
1271 return add_text_properties_1 (start
, end
, properties
, object
,
1272 TEXT_PROPERTY_REPLACE
);
1275 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1277 DEFUN ("put-text-property", Fput_text_property
,
1278 Sput_text_property
, 4, 5, 0,
1279 doc
: /* Set one property of the text from START to END.
1280 The third and fourth arguments PROPERTY and VALUE
1281 specify the property to add.
1282 If the optional fifth argument OBJECT is a buffer (or nil, which means
1283 the current buffer), START and END are buffer positions (integers or
1284 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1285 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
,
1286 Lisp_Object value
, Lisp_Object object
)
1288 AUTO_LIST2 (properties
, property
, value
);
1289 Fadd_text_properties (start
, end
, properties
, object
);
1293 DEFUN ("set-text-properties", Fset_text_properties
,
1294 Sset_text_properties
, 3, 4, 0,
1295 doc
: /* Completely replace properties of text from START to END.
1296 The third argument PROPERTIES is the new property list.
1297 If the optional fourth argument OBJECT is a buffer (or nil, which means
1298 the current buffer), START and END are buffer positions (integers or
1299 markers). If OBJECT is a string, START and END are 0-based indices into it.
1300 If PROPERTIES is nil, the effect is to remove all properties from
1301 the designated part of OBJECT. */)
1302 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1304 return set_text_properties (start
, end
, properties
, object
, Qt
);
1308 DEFUN ("add-face-text-property", Fadd_face_text_property
,
1309 Sadd_face_text_property
, 3, 5, 0,
1310 doc
: /* Add the face property to the text from START to END.
1311 FACE specifies the face to add. It should be a valid value of the
1312 `face' property (typically a face name or a plist of face attributes
1315 If any text in the region already has a non-nil `face' property, those
1316 face(s) are retained. This is done by setting the `face' property to
1317 a list of faces, with FACE as the first element (by default) and the
1318 pre-existing faces as the remaining elements.
1320 If optional fourth argument APPEND is non-nil, append FACE to the end
1321 of the face list instead.
1323 If optional fifth argument OBJECT is a buffer (or nil, which means the
1324 current buffer), START and END are buffer positions (integers or
1325 markers). If OBJECT is a string, START and END are 0-based indices
1327 (Lisp_Object start
, Lisp_Object end
, Lisp_Object face
,
1328 Lisp_Object append
, Lisp_Object object
)
1330 AUTO_LIST2 (properties
, Qface
, face
);
1331 add_text_properties_1 (start
, end
, properties
, object
,
1333 ? TEXT_PROPERTY_PREPEND
1334 : TEXT_PROPERTY_APPEND
));
1338 /* Replace properties of text from START to END with new list of
1339 properties PROPERTIES. OBJECT is the buffer or string containing
1340 the text. OBJECT nil means use the current buffer.
1341 COHERENT_CHANGE_P nil means this is being called as an internal
1342 subroutine, rather than as a change primitive with checking of
1343 read-only, invoking change hooks, etc.. Value is nil if the
1344 function _detected_ that it did not replace any properties, non-nil
1348 set_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
,
1349 Lisp_Object object
, Lisp_Object coherent_change_p
)
1351 register INTERVAL i
;
1352 Lisp_Object ostart
, oend
;
1357 properties
= validate_plist (properties
);
1360 XSETBUFFER (object
, current_buffer
);
1362 /* If we want no properties for a whole string,
1363 get rid of its intervals. */
1364 if (NILP (properties
) && STRINGP (object
)
1365 && XFASTINT (start
) == 0
1366 && XFASTINT (end
) == SCHARS (object
))
1368 if (!string_intervals (object
))
1371 set_string_intervals (object
, NULL
);
1375 i
= validate_interval_range (object
, &start
, &end
, soft
);
1379 /* If buffer has no properties, and we want none, return now. */
1380 if (NILP (properties
))
1383 /* Restore the original START and END values
1384 because validate_interval_range increments them for strings. */
1388 i
= validate_interval_range (object
, &start
, &end
, hard
);
1389 /* This can return if start == end. */
1394 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1395 modify_text_properties (object
, start
, end
);
1397 set_text_properties_1 (start
, end
, properties
, object
, i
);
1399 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1400 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1401 XINT (end
) - XINT (start
));
1405 /* Replace properties of text from START to END with new list of
1406 properties PROPERTIES. OBJECT is the buffer or string containing
1407 the text. This does not obey any hooks.
1408 You should provide the interval that START is located in as I.
1409 START and END can be in any order. */
1412 set_text_properties_1 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
, INTERVAL i
)
1414 register INTERVAL prev_changed
= NULL
;
1415 register ptrdiff_t s
, len
;
1418 if (XINT (start
) < XINT (end
))
1421 len
= XINT (end
) - s
;
1423 else if (XINT (end
) < XINT (start
))
1426 len
= XINT (start
) - s
;
1433 if (i
->position
!= s
)
1436 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1438 if (LENGTH (i
) > len
)
1440 copy_properties (unchanged
, i
);
1441 i
= split_interval_left (i
, len
);
1442 set_properties (properties
, i
, object
);
1446 set_properties (properties
, i
, object
);
1448 if (LENGTH (i
) == len
)
1453 i
= next_interval (i
);
1456 /* We are starting at the beginning of an interval I. LEN is positive. */
1461 if (LENGTH (i
) >= len
)
1463 if (LENGTH (i
) > len
)
1464 i
= split_interval_left (i
, len
);
1466 /* We have to call set_properties even if we are going to
1467 merge the intervals, so as to make the undo records
1468 and cause redisplay to happen. */
1469 set_properties (properties
, i
, object
);
1471 merge_interval_left (i
);
1477 /* We have to call set_properties even if we are going to
1478 merge the intervals, so as to make the undo records
1479 and cause redisplay to happen. */
1480 set_properties (properties
, i
, object
);
1484 prev_changed
= i
= merge_interval_left (i
);
1486 i
= next_interval (i
);
1491 DEFUN ("remove-text-properties", Fremove_text_properties
,
1492 Sremove_text_properties
, 3, 4, 0,
1493 doc
: /* Remove some properties from text from START to END.
1494 The third argument PROPERTIES is a property list
1495 whose property names specify the properties to remove.
1496 \(The values stored in PROPERTIES are ignored.)
1497 If the optional fourth argument OBJECT is a buffer (or nil, which means
1498 the current buffer), START and END are buffer positions (integers or
1499 markers). If OBJECT is a string, START and END are 0-based indices into it.
1500 Return t if any property was actually removed, nil otherwise.
1502 Use `set-text-properties' if you want to remove all text properties. */)
1503 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1505 INTERVAL i
, unchanged
;
1507 bool modified
= false;
1508 bool first_time
= true;
1511 XSETBUFFER (object
, current_buffer
);
1514 i
= validate_interval_range (object
, &start
, &end
, soft
);
1519 len
= XINT (end
) - s
;
1521 /* If there are no properties on this entire interval, return. */
1522 if (! interval_has_some_properties (properties
, i
))
1524 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1531 i
= next_interval (i
);
1534 while (! interval_has_some_properties (properties
, i
));
1536 /* Split away the beginning of this interval; what we don't
1538 else if (i
->position
!= s
)
1541 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1542 copy_properties (unchanged
, i
);
1545 if (BUFFERP (object
) && first_time
)
1547 ptrdiff_t prev_total_length
= TOTAL_LENGTH (i
);
1548 ptrdiff_t prev_pos
= i
->position
;
1550 modify_text_properties (object
, start
, end
);
1551 /* If someone called us recursively as a side effect of
1552 modify_text_properties, and changed the intervals behind our back
1553 (could happen if lock_file, called by prepare_to_modify_buffer,
1554 triggers redisplay, and that calls add-text-properties again
1555 in the same buffer), we cannot continue with I, because its
1556 data changed. So we restart the interval analysis anew. */
1557 if (TOTAL_LENGTH (i
) != prev_total_length
1558 || i
->position
!= prev_pos
)
1565 /* We are at the beginning of an interval, with len to scan */
1570 if (LENGTH (i
) >= len
)
1572 if (! interval_has_some_properties (properties
, i
))
1575 if (BUFFERP (object
))
1576 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1577 XINT (end
) - XINT (start
));
1581 if (LENGTH (i
) == len
)
1583 remove_properties (properties
, Qnil
, i
, object
);
1584 if (BUFFERP (object
))
1585 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1586 XINT (end
) - XINT (start
));
1590 /* i has the properties, and goes past the change limit */
1592 i
= split_interval_left (i
, len
);
1593 copy_properties (unchanged
, i
);
1594 remove_properties (properties
, Qnil
, i
, object
);
1595 if (BUFFERP (object
))
1596 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1597 XINT (end
) - XINT (start
));
1602 modified
|= remove_properties (properties
, Qnil
, i
, object
);
1603 i
= next_interval (i
);
1607 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1608 Sremove_list_of_text_properties
, 3, 4, 0,
1609 doc
: /* Remove some properties from text from START to END.
1610 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1611 If the optional fourth argument OBJECT is a buffer (or nil, which means
1612 the current buffer), START and END are buffer positions (integers or
1613 markers). If OBJECT is a string, START and END are 0-based indices into it.
1614 Return t if any property was actually removed, nil otherwise. */)
1615 (Lisp_Object start
, Lisp_Object end
, Lisp_Object list_of_properties
, Lisp_Object object
)
1617 INTERVAL i
, unchanged
;
1619 bool modified
= false;
1620 Lisp_Object properties
;
1621 properties
= list_of_properties
;
1624 XSETBUFFER (object
, current_buffer
);
1626 i
= validate_interval_range (object
, &start
, &end
, soft
);
1631 len
= XINT (end
) - s
;
1633 /* If there are no properties on the interval, return. */
1634 if (! interval_has_some_properties_list (properties
, i
))
1636 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1643 i
= next_interval (i
);
1646 while (! interval_has_some_properties_list (properties
, i
));
1648 /* Split away the beginning of this interval; what we don't
1650 else if (i
->position
!= s
)
1653 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1654 copy_properties (unchanged
, i
);
1657 /* We are at the beginning of an interval, with len to scan.
1658 The flag MODIFIED records if changes have been made.
1659 When object is a buffer, we must call modify_text_properties
1660 before changes are made and signal_after_change when we are done.
1661 Call modify_text_properties before calling remove_properties if !MODIFIED,
1662 and call signal_after_change before returning if MODIFIED. */
1667 if (LENGTH (i
) >= len
)
1669 if (! interval_has_some_properties_list (properties
, i
))
1673 if (BUFFERP (object
))
1674 signal_after_change (XINT (start
),
1675 XINT (end
) - XINT (start
),
1676 XINT (end
) - XINT (start
));
1682 else if (LENGTH (i
) == len
)
1684 if (!modified
&& BUFFERP (object
))
1685 modify_text_properties (object
, start
, end
);
1686 remove_properties (Qnil
, properties
, i
, object
);
1687 if (BUFFERP (object
))
1688 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1689 XINT (end
) - XINT (start
));
1693 { /* i has the properties, and goes past the change limit. */
1695 i
= split_interval_left (i
, len
);
1696 copy_properties (unchanged
, i
);
1697 if (!modified
&& BUFFERP (object
))
1698 modify_text_properties (object
, start
, end
);
1699 remove_properties (Qnil
, properties
, i
, object
);
1700 if (BUFFERP (object
))
1701 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1702 XINT (end
) - XINT (start
));
1706 if (interval_has_some_properties_list (properties
, i
))
1708 if (!modified
&& BUFFERP (object
))
1709 modify_text_properties (object
, start
, end
);
1710 remove_properties (Qnil
, properties
, i
, object
);
1714 i
= next_interval (i
);
1719 if (BUFFERP (object
))
1720 signal_after_change (XINT (start
),
1721 XINT (end
) - XINT (start
),
1722 XINT (end
) - XINT (start
));
1731 DEFUN ("text-property-any", Ftext_property_any
,
1732 Stext_property_any
, 4, 5, 0,
1733 doc
: /* Check text from START to END for property PROPERTY equaling VALUE.
1734 If so, return the position of the first character whose property PROPERTY
1735 is `eq' to VALUE. Otherwise return nil.
1736 If the optional fifth argument OBJECT is a buffer (or nil, which means
1737 the current buffer), START and END are buffer positions (integers or
1738 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1739 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1741 register INTERVAL i
;
1742 register ptrdiff_t e
, pos
;
1745 XSETBUFFER (object
, current_buffer
);
1746 i
= validate_interval_range (object
, &start
, &end
, soft
);
1748 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1753 if (i
->position
>= e
)
1755 if (EQ (textget (i
->plist
, property
), value
))
1758 if (pos
< XINT (start
))
1760 return make_number (pos
);
1762 i
= next_interval (i
);
1767 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1768 Stext_property_not_all
, 4, 5, 0,
1769 doc
: /* Check text from START to END for property PROPERTY not equaling VALUE.
1770 If so, return the position of the first character whose property PROPERTY
1771 is not `eq' to VALUE. Otherwise, return nil.
1772 If the optional fifth argument OBJECT is a buffer (or nil, which means
1773 the current buffer), START and END are buffer positions (integers or
1774 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1775 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1777 register INTERVAL i
;
1778 register ptrdiff_t s
, e
;
1781 XSETBUFFER (object
, current_buffer
);
1782 i
= validate_interval_range (object
, &start
, &end
, soft
);
1784 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1790 if (i
->position
>= e
)
1792 if (! EQ (textget (i
->plist
, property
), value
))
1794 if (i
->position
> s
)
1796 return make_number (s
);
1798 i
= next_interval (i
);
1804 /* Return the direction from which the text-property PROP would be
1805 inherited by any new text inserted at POS: 1 if it would be
1806 inherited from the char after POS, -1 if it would be inherited from
1807 the char before POS, and 0 if from neither.
1808 BUFFER can be either a buffer or nil (meaning current buffer). */
1811 text_property_stickiness (Lisp_Object prop
, Lisp_Object pos
, Lisp_Object buffer
)
1813 bool ignore_previous_character
;
1814 Lisp_Object prev_pos
= make_number (XINT (pos
) - 1);
1815 Lisp_Object front_sticky
;
1816 bool is_rear_sticky
= true, is_front_sticky
= false; /* defaults */
1817 Lisp_Object defalt
= Fassq (prop
, Vtext_property_default_nonsticky
);
1820 XSETBUFFER (buffer
, current_buffer
);
1822 ignore_previous_character
= XINT (pos
) <= BUF_BEGV (XBUFFER (buffer
));
1824 if (ignore_previous_character
|| (CONSP (defalt
) && !NILP (XCDR (defalt
))))
1825 is_rear_sticky
= false;
1828 Lisp_Object rear_non_sticky
1829 = Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1831 if (!NILP (CONSP (rear_non_sticky
)
1832 ? Fmemq (prop
, rear_non_sticky
)
1834 /* PROP is rear-non-sticky. */
1835 is_rear_sticky
= false;
1838 /* Consider following character. */
1839 /* This signals an arg-out-of-range error if pos is outside the
1840 buffer's accessible range. */
1841 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1843 if (EQ (front_sticky
, Qt
)
1844 || (CONSP (front_sticky
)
1845 && !NILP (Fmemq (prop
, front_sticky
))))
1846 /* PROP is inherited from after. */
1847 is_front_sticky
= true;
1849 /* Simple cases, where the properties are consistent. */
1850 if (is_rear_sticky
&& !is_front_sticky
)
1852 else if (!is_rear_sticky
&& is_front_sticky
)
1854 else if (!is_rear_sticky
&& !is_front_sticky
)
1857 /* The stickiness properties are inconsistent, so we have to
1858 disambiguate. Basically, rear-sticky wins, _except_ if the
1859 property that would be inherited has a value of nil, in which case
1860 front-sticky wins. */
1861 if (ignore_previous_character
1862 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1869 /* Copying properties between objects. */
1871 /* Add properties from START to END of SRC, starting at POS in DEST.
1872 SRC and DEST may each refer to strings or buffers.
1873 Optional sixth argument PROP causes only that property to be copied.
1874 Properties are copied to DEST as if by `add-text-properties'.
1875 Return t if any property value actually changed, nil otherwise. */
1877 /* Note this can GC when DEST is a buffer. */
1880 copy_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object src
,
1881 Lisp_Object pos
, Lisp_Object dest
, Lisp_Object prop
)
1887 ptrdiff_t s
, e
, e2
, p
, len
;
1888 bool modified
= false;
1890 i
= validate_interval_range (src
, &start
, &end
, soft
);
1894 CHECK_NUMBER_COERCE_MARKER (pos
);
1896 Lisp_Object dest_start
, dest_end
;
1898 e
= XINT (pos
) + (XINT (end
) - XINT (start
));
1899 if (MOST_POSITIVE_FIXNUM
< e
)
1900 args_out_of_range (pos
, end
);
1902 XSETFASTINT (dest_end
, e
);
1903 /* Apply this to a copy of pos; it will try to increment its arguments,
1904 which we don't want. */
1905 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1916 e2
= i
->position
+ LENGTH (i
);
1923 while (! NILP (plist
))
1925 if (EQ (Fcar (plist
), prop
))
1927 plist
= list2 (prop
, Fcar (Fcdr (plist
)));
1930 plist
= Fcdr (Fcdr (plist
));
1933 /* Must defer modifications to the interval tree in case
1934 src and dest refer to the same string or buffer. */
1935 stuff
= Fcons (list3 (make_number (p
), make_number (p
+ len
), plist
),
1938 i
= next_interval (i
);
1946 while (! NILP (stuff
))
1949 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1950 Fcar (Fcdr (Fcdr (res
))), dest
);
1953 stuff
= Fcdr (stuff
);
1956 return modified
? Qt
: Qnil
;
1960 /* Return a list representing the text properties of OBJECT between
1961 START and END. if PROP is non-nil, report only on that property.
1962 Each result list element has the form (S E PLIST), where S and E
1963 are positions in OBJECT and PLIST is a property list containing the
1964 text properties of OBJECT between S and E. Value is nil if OBJECT
1965 doesn't contain text properties between START and END. */
1968 text_property_list (Lisp_Object object
, Lisp_Object start
, Lisp_Object end
, Lisp_Object prop
)
1975 i
= validate_interval_range (object
, &start
, &end
, soft
);
1978 ptrdiff_t s
= XINT (start
);
1979 ptrdiff_t e
= XINT (end
);
1983 ptrdiff_t interval_end
, len
;
1986 interval_end
= i
->position
+ LENGTH (i
);
1987 if (interval_end
> e
)
1989 len
= interval_end
- s
;
1994 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1995 if (EQ (XCAR (plist
), prop
))
1997 plist
= list2 (prop
, Fcar (XCDR (plist
)));
2002 result
= Fcons (list3 (make_number (s
), make_number (s
+ len
),
2006 i
= next_interval (i
);
2017 /* Add text properties to OBJECT from LIST. LIST is a list of triples
2018 (START END PLIST), where START and END are positions and PLIST is a
2019 property list containing the text properties to add. Adjust START
2020 and END positions by DELTA before adding properties. */
2023 add_text_properties_from_list (Lisp_Object object
, Lisp_Object list
, Lisp_Object delta
)
2025 for (; CONSP (list
); list
= XCDR (list
))
2027 Lisp_Object item
, start
, end
, plist
;
2030 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
2031 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
2032 plist
= XCAR (XCDR (XCDR (item
)));
2034 Fadd_text_properties (start
, end
, plist
, object
);
2040 /* Modify end-points of ranges in LIST destructively, and return the
2041 new list. LIST is a list as returned from text_property_list.
2042 Discard properties that begin at or after NEW_END, and limit
2043 end-points to NEW_END. */
2046 extend_property_ranges (Lisp_Object list
, Lisp_Object old_end
, Lisp_Object new_end
)
2048 Lisp_Object prev
= Qnil
, head
= list
;
2049 ptrdiff_t max
= XINT (new_end
);
2051 for (; CONSP (list
); prev
= list
, list
= XCDR (list
))
2053 Lisp_Object item
, beg
;
2058 end
= XINT (XCAR (XCDR (item
)));
2060 if (XINT (beg
) >= max
)
2062 /* The start-point is past the end of the new string.
2063 Discard this property. */
2064 if (EQ (head
, list
))
2067 XSETCDR (prev
, XCDR (list
));
2069 else if ((end
== XINT (old_end
) && end
!= max
)
2072 /* Either the end-point is past the end of the new string,
2073 and we need to discard the properties past the new end,
2074 or the caller is extending the property range, and we
2075 should update all end-points that are on the old end of
2076 the range to reflect that. */
2077 XSETCAR (XCDR (item
), new_end
);
2086 /* Call the modification hook functions in LIST, each with START and END. */
2089 call_mod_hooks (Lisp_Object list
, Lisp_Object start
, Lisp_Object end
)
2091 while (!NILP (list
))
2093 call2 (Fcar (list
), start
, end
);
2098 /* Check for read-only intervals between character positions START ... END,
2099 in BUF, and signal an error if we find one.
2101 Then check for any modification hooks in the range.
2102 Create a list of all these hooks in lexicographic order,
2103 eliminating consecutive extra copies of the same hook. Then call
2104 those hooks in order, with START and END - 1 as arguments. */
2107 verify_interval_modification (struct buffer
*buf
,
2108 ptrdiff_t start
, ptrdiff_t end
)
2110 INTERVAL intervals
= buffer_intervals (buf
);
2113 Lisp_Object prev_mod_hooks
;
2114 Lisp_Object mod_hooks
;
2117 prev_mod_hooks
= Qnil
;
2120 interval_insert_behind_hooks
= Qnil
;
2121 interval_insert_in_front_hooks
= Qnil
;
2128 ptrdiff_t temp
= start
;
2133 /* For an insert operation, check the two chars around the position. */
2136 INTERVAL prev
= NULL
;
2137 Lisp_Object before
, after
;
2139 /* Set I to the interval containing the char after START,
2140 and PREV to the interval containing the char before START.
2141 Either one may be null. They may be equal. */
2142 i
= find_interval (intervals
, start
);
2144 if (start
== BUF_BEGV (buf
))
2146 else if (i
->position
== start
)
2147 prev
= previous_interval (i
);
2148 else if (i
->position
< start
)
2150 if (start
== BUF_ZV (buf
))
2153 /* If Vinhibit_read_only is set and is not a list, we can
2154 skip the read_only checks. */
2155 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2157 /* If I and PREV differ we need to check for the read-only
2158 property together with its stickiness. If either I or
2159 PREV are 0, this check is all we need.
2160 We have to take special care, since read-only may be
2161 indirectly defined via the category property. */
2166 after
= textget (i
->plist
, Qread_only
);
2168 /* If interval I is read-only and read-only is
2169 front-sticky, inhibit insertion.
2170 Check for read-only as well as category. */
2172 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2176 tem
= textget (i
->plist
, Qfront_sticky
);
2177 if (TMEM (Qread_only
, tem
)
2178 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2179 && TMEM (Qcategory
, tem
)))
2180 text_read_only (after
);
2186 before
= textget (prev
->plist
, Qread_only
);
2188 /* If interval PREV is read-only and read-only isn't
2189 rear-nonsticky, inhibit insertion.
2190 Check for read-only as well as category. */
2192 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2196 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2197 if (! TMEM (Qread_only
, tem
)
2198 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2199 || ! TMEM (Qcategory
, tem
)))
2200 text_read_only (before
);
2206 after
= textget (i
->plist
, Qread_only
);
2208 /* If interval I is read-only and read-only is
2209 front-sticky, inhibit insertion.
2210 Check for read-only as well as category. */
2211 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2215 tem
= textget (i
->plist
, Qfront_sticky
);
2216 if (TMEM (Qread_only
, tem
)
2217 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2218 && TMEM (Qcategory
, tem
)))
2219 text_read_only (after
);
2221 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2222 if (! TMEM (Qread_only
, tem
)
2223 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2224 || ! TMEM (Qcategory
, tem
)))
2225 text_read_only (after
);
2230 /* Run both insert hooks (just once if they're the same). */
2232 interval_insert_behind_hooks
2233 = textget (prev
->plist
, Qinsert_behind_hooks
);
2235 interval_insert_in_front_hooks
2236 = textget (i
->plist
, Qinsert_in_front_hooks
);
2240 /* Loop over intervals on or next to START...END,
2241 collecting their hooks. */
2243 i
= find_interval (intervals
, start
);
2246 if (! INTERVAL_WRITABLE_P (i
))
2247 text_read_only (textget (i
->plist
, Qread_only
));
2249 if (!inhibit_modification_hooks
)
2251 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2252 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2254 hooks
= Fcons (mod_hooks
, hooks
);
2255 prev_mod_hooks
= mod_hooks
;
2259 if (i
->position
+ LENGTH (i
) < end
2260 && (!NILP (BVAR (current_buffer
, read_only
))
2261 && NILP (Vinhibit_read_only
)))
2262 xsignal1 (Qbuffer_read_only
, Fcurrent_buffer ());
2264 i
= next_interval (i
);
2266 /* Keep going thru the interval containing the char before END. */
2267 while (i
&& i
->position
< end
);
2269 if (!inhibit_modification_hooks
)
2271 hooks
= Fnreverse (hooks
);
2272 while (! EQ (hooks
, Qnil
))
2274 call_mod_hooks (Fcar (hooks
), make_number (start
),
2276 hooks
= Fcdr (hooks
);
2282 /* Run the interval hooks for an insertion on character range START ... END.
2283 verify_interval_modification chose which hooks to run;
2284 this function is called after the insertion happens
2285 so it can indicate the range of inserted text. */
2288 report_interval_modification (Lisp_Object start
, Lisp_Object end
)
2290 if (! NILP (interval_insert_behind_hooks
))
2291 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2292 if (! NILP (interval_insert_in_front_hooks
)
2293 && ! EQ (interval_insert_in_front_hooks
,
2294 interval_insert_behind_hooks
))
2295 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2299 syms_of_textprop (void)
2301 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties
,
2302 doc
: /* Property-list used as default values.
2303 The value of a property in this list is seen as the value for every
2304 character that does not have its own value for that property. */);
2305 Vdefault_text_properties
= Qnil
;
2307 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist
,
2308 doc
: /* Alist of alternative properties for properties without a value.
2309 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2310 If a piece of text has no direct value for a particular property, then
2311 this alist is consulted. If that property appears in the alist, then
2312 the first non-nil value from the associated alternative properties is
2314 Vchar_property_alias_alist
= Qnil
;
2316 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks
,
2317 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2318 This also inhibits the use of the `intangible' text property.
2320 This variable is obsolete since Emacs-25.1. Use `cursor-intangible-mode'
2321 or `cursor-sensor-mode' instead. */);
2322 /* FIXME: We should make-obsolete-variable, but that signals too many
2323 warnings in code which does (let ((inhibit-point-motion-hooks t)) ...)
2324 Ideally, make-obsolete-variable should let us specify that only the nil
2325 value is obsolete, but that requires too many changes in bytecomp.el,
2326 so for now we'll keep it "obsolete via the docstring". */
2327 Vinhibit_point_motion_hooks
= Qt
;
2329 DEFVAR_LISP ("text-property-default-nonsticky",
2330 Vtext_property_default_nonsticky
,
2331 doc
: /* Alist of properties vs the corresponding non-stickiness.
2332 Each element has the form (PROPERTY . NONSTICKINESS).
2334 If a character in a buffer has PROPERTY, new text inserted adjacent to
2335 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2336 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2337 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2338 /* Text properties `syntax-table'and `display' should be nonsticky
2340 Vtext_property_default_nonsticky
2341 = list2 (Fcons (Qsyntax_table
, Qt
), Fcons (Qdisplay
, Qt
));
2343 staticpro (&interval_insert_behind_hooks
);
2344 staticpro (&interval_insert_in_front_hooks
);
2345 interval_insert_behind_hooks
= Qnil
;
2346 interval_insert_in_front_hooks
= Qnil
;
2349 /* Common attributes one might give text. */
2351 DEFSYM (Qfont
, "font");
2352 DEFSYM (Qface
, "face");
2353 DEFSYM (Qread_only
, "read-only");
2354 DEFSYM (Qinvisible
, "invisible");
2355 DEFSYM (Qintangible
, "intangible");
2356 DEFSYM (Qcategory
, "category");
2357 DEFSYM (Qlocal_map
, "local-map");
2358 DEFSYM (Qfront_sticky
, "front-sticky");
2359 DEFSYM (Qrear_nonsticky
, "rear-nonsticky");
2360 DEFSYM (Qmouse_face
, "mouse-face");
2361 DEFSYM (Qminibuffer_prompt
, "minibuffer-prompt");
2363 /* Properties that text might use to specify certain actions. */
2365 DEFSYM (Qpoint_left
, "point-left");
2366 DEFSYM (Qpoint_entered
, "point-entered");
2368 defsubr (&Stext_properties_at
);
2369 defsubr (&Sget_text_property
);
2370 defsubr (&Sget_char_property
);
2371 defsubr (&Sget_char_property_and_overlay
);
2372 defsubr (&Snext_char_property_change
);
2373 defsubr (&Sprevious_char_property_change
);
2374 defsubr (&Snext_single_char_property_change
);
2375 defsubr (&Sprevious_single_char_property_change
);
2376 defsubr (&Snext_property_change
);
2377 defsubr (&Snext_single_property_change
);
2378 defsubr (&Sprevious_property_change
);
2379 defsubr (&Sprevious_single_property_change
);
2380 defsubr (&Sadd_text_properties
);
2381 defsubr (&Sput_text_property
);
2382 defsubr (&Sset_text_properties
);
2383 defsubr (&Sadd_face_text_property
);
2384 defsubr (&Sremove_text_properties
);
2385 defsubr (&Sremove_list_of_text_properties
);
2386 defsubr (&Stext_property_any
);
2387 defsubr (&Stext_property_not_all
);