1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2015 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 enum property_set_type
50 TEXT_PROPERTY_REPLACE
,
51 TEXT_PROPERTY_PREPEND
,
55 /* If o1 is a cons whose cdr is a cons, return true and set o2 to
56 the o1's cdr. Otherwise, return false. This is handy for
58 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
60 /* verify_interval_modification saves insertion hooks here
61 to be run later by report_interval_modification. */
62 static Lisp_Object interval_insert_behind_hooks
;
63 static Lisp_Object interval_insert_in_front_hooks
;
66 /* Signal a `text-read-only' error. This function makes it easier
67 to capture that error in GDB by putting a breakpoint on it. */
70 text_read_only (Lisp_Object propval
)
72 if (STRINGP (propval
))
73 xsignal1 (Qtext_read_only
, propval
);
75 xsignal0 (Qtext_read_only
);
78 /* Prepare to modify the text properties of BUFFER from START to END. */
81 modify_text_properties (Lisp_Object buffer
, Lisp_Object start
, Lisp_Object end
)
83 ptrdiff_t b
= XINT (start
), e
= XINT (end
);
84 struct buffer
*buf
= XBUFFER (buffer
), *old
= current_buffer
;
86 set_buffer_internal (buf
);
88 prepare_to_modify_buffer_1 (b
, e
, NULL
);
90 BUF_COMPUTE_UNCHANGED (buf
, b
- 1, e
);
91 if (MODIFF
<= SAVE_MODIFF
)
92 record_first_change ();
95 bset_point_before_scroll (current_buffer
, Qnil
);
97 set_buffer_internal (old
);
100 /* Complain if object is not string or buffer type. */
103 CHECK_STRING_OR_BUFFER (Lisp_Object x
)
105 CHECK_TYPE (STRINGP (x
) || BUFFERP (x
), Qbuffer_or_string_p
, x
);
108 /* Extract the interval at the position pointed to by BEGIN from
109 OBJECT, a string or buffer. Additionally, check that the positions
110 pointed to by BEGIN and END are within the bounds of OBJECT, and
111 reverse them if *BEGIN is greater than *END. The objects pointed
112 to by BEGIN and END may be integers or markers; if the latter, they
113 are coerced to integers.
115 When OBJECT is a string, we increment *BEGIN and *END
116 to make them origin-one.
118 Note that buffer points don't correspond to interval indices.
119 For example, point-max is 1 greater than the index of the last
120 character. This difference is handled in the caller, which uses
121 the validated points to determine a length, and operates on that.
122 Exceptions are Ftext_properties_at, Fnext_property_change, and
123 Fprevious_property_change which call this function with BEGIN == END.
124 Handle this case specially.
126 If FORCE is soft (false), it's OK to return NULL. Otherwise,
127 create an interval tree for OBJECT if one doesn't exist, provided
128 the object actually contains text. In the current design, if there
129 is no text, there can be no text properties. */
131 enum { soft
= false, hard
= true };
134 validate_interval_range (Lisp_Object object
, Lisp_Object
*begin
,
135 Lisp_Object
*end
, bool force
)
140 CHECK_STRING_OR_BUFFER (object
);
141 CHECK_NUMBER_COERCE_MARKER (*begin
);
142 CHECK_NUMBER_COERCE_MARKER (*end
);
144 /* If we are asked for a point, but from a subr which operates
145 on a range, then return nothing. */
146 if (EQ (*begin
, *end
) && begin
!= end
)
149 if (XINT (*begin
) > XINT (*end
))
157 if (BUFFERP (object
))
159 register struct buffer
*b
= XBUFFER (object
);
161 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
162 && XINT (*end
) <= BUF_ZV (b
)))
163 args_out_of_range (*begin
, *end
);
164 i
= buffer_intervals (b
);
166 /* If there's no text, there are no properties. */
167 if (BUF_BEGV (b
) == BUF_ZV (b
))
170 searchpos
= XINT (*begin
);
174 ptrdiff_t len
= SCHARS (object
);
176 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
177 && XINT (*end
) <= len
))
178 args_out_of_range (*begin
, *end
);
179 XSETFASTINT (*begin
, XFASTINT (*begin
));
181 XSETFASTINT (*end
, XFASTINT (*end
));
182 i
= string_intervals (object
);
187 searchpos
= XINT (*begin
);
191 return (force
? create_root_interval (object
) : i
);
193 return find_interval (i
, searchpos
);
196 /* Validate LIST as a property list. If LIST is not a list, then
197 make one consisting of (LIST nil). Otherwise, verify that LIST
198 is even numbered and thus suitable as a plist. */
201 validate_plist (Lisp_Object list
)
208 Lisp_Object tail
= list
;
213 error ("Odd length text property list");
217 while (CONSP (tail
));
222 return list2 (list
, Qnil
);
225 /* Return true if interval I has all the properties,
226 with the same values, of list PLIST. */
229 interval_has_all_properties (Lisp_Object plist
, INTERVAL i
)
231 Lisp_Object tail1
, tail2
;
233 /* Go through each element of PLIST. */
234 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
236 Lisp_Object sym1
= XCAR (tail1
);
239 /* Go through I's plist, looking for sym1 */
240 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
241 if (EQ (sym1
, XCAR (tail2
)))
243 /* Found the same property on both lists. If the
244 values are unequal, return false. */
245 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
248 /* Property has same value on both lists; go to next one. */
260 /* Return true if the plist of interval I has any of the
261 properties of PLIST, regardless of their values. */
264 interval_has_some_properties (Lisp_Object plist
, INTERVAL i
)
266 Lisp_Object tail1
, tail2
, sym
;
268 /* Go through each element of PLIST. */
269 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
273 /* Go through i's plist, looking for tail1 */
274 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
275 if (EQ (sym
, XCAR (tail2
)))
282 /* Return true if the plist of interval I has any of the
283 property names in LIST, regardless of their values. */
286 interval_has_some_properties_list (Lisp_Object list
, INTERVAL i
)
288 Lisp_Object tail1
, tail2
, sym
;
290 /* Go through each element of LIST. */
291 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
295 /* Go through i's plist, looking for tail1 */
296 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
297 if (EQ (sym
, XCAR (tail2
)))
304 /* Changing the plists of individual intervals. */
306 /* Return the value of PROP in property-list PLIST, or Qunbound if it
309 property_value (Lisp_Object plist
, Lisp_Object prop
)
313 while (PLIST_ELT_P (plist
, value
))
314 if (EQ (XCAR (plist
), prop
))
317 plist
= XCDR (value
);
322 /* Set the properties of INTERVAL to PROPERTIES,
323 and record undo info for the previous values.
324 OBJECT is the string or buffer that INTERVAL belongs to. */
327 set_properties (Lisp_Object properties
, INTERVAL interval
, Lisp_Object object
)
329 Lisp_Object sym
, value
;
331 if (BUFFERP (object
))
333 /* For each property in the old plist which is missing from PROPERTIES,
334 or has a different value in PROPERTIES, make an undo record. */
335 for (sym
= interval
->plist
;
336 PLIST_ELT_P (sym
, value
);
338 if (! EQ (property_value (properties
, XCAR (sym
)),
341 record_property_change (interval
->position
, LENGTH (interval
),
342 XCAR (sym
), XCAR (value
),
346 /* For each new property that has no value at all in the old plist,
347 make an undo record binding it to nil, so it will be removed. */
348 for (sym
= properties
;
349 PLIST_ELT_P (sym
, value
);
351 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
353 record_property_change (interval
->position
, LENGTH (interval
),
359 /* Store new properties. */
360 set_interval_plist (interval
, Fcopy_sequence (properties
));
363 /* Add the properties of PLIST to the interval I, or set
364 the value of I's property to the value of the property on PLIST
365 if they are different.
367 OBJECT should be the string or buffer the interval is in.
369 Return true if this changes I (i.e., if any members of PLIST
370 are actually added to I's plist) */
373 add_properties (Lisp_Object plist
, INTERVAL i
, Lisp_Object object
,
374 enum property_set_type set_type
)
376 Lisp_Object tail1
, tail2
, sym1
, val1
;
377 bool changed
= false;
383 /* Go through each element of PLIST. */
384 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
388 val1
= Fcar (XCDR (tail1
));
390 /* Go through I's plist, looking for sym1 */
391 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
392 if (EQ (sym1
, XCAR (tail2
)))
394 Lisp_Object this_cdr
;
396 this_cdr
= XCDR (tail2
);
397 /* Found the property. Now check its value. */
400 /* The properties have the same value on both lists.
401 Continue to the next property. */
402 if (EQ (val1
, Fcar (this_cdr
)))
405 /* Record this change in the buffer, for undo purposes. */
406 if (BUFFERP (object
))
408 record_property_change (i
->position
, LENGTH (i
),
409 sym1
, Fcar (this_cdr
), object
);
412 /* I's property has a different value -- change it */
413 if (set_type
== TEXT_PROPERTY_REPLACE
)
414 Fsetcar (this_cdr
, val1
);
416 if (CONSP (Fcar (this_cdr
)) &&
417 /* Special-case anonymous face properties. */
418 (! EQ (sym1
, Qface
) ||
419 NILP (Fkeywordp (Fcar (Fcar (this_cdr
))))))
420 /* The previous value is a list, so prepend (or
421 append) the new value to this list. */
422 if (set_type
== TEXT_PROPERTY_PREPEND
)
423 Fsetcar (this_cdr
, Fcons (val1
, Fcar (this_cdr
)));
425 nconc2 (Fcar (this_cdr
), list1 (val1
));
427 /* The previous value is a single value, so make it
429 if (set_type
== TEXT_PROPERTY_PREPEND
)
430 Fsetcar (this_cdr
, list2 (val1
, Fcar (this_cdr
)));
432 Fsetcar (this_cdr
, list2 (Fcar (this_cdr
), val1
));
441 /* Record this change in the buffer, for undo purposes. */
442 if (BUFFERP (object
))
444 record_property_change (i
->position
, LENGTH (i
),
447 set_interval_plist (i
, Fcons (sym1
, Fcons (val1
, i
->plist
)));
455 /* For any members of PLIST, or LIST,
456 which are properties of I, remove them from I's plist.
457 (If PLIST is non-nil, use that, otherwise use LIST.)
458 OBJECT is the string or buffer containing I. */
461 remove_properties (Lisp_Object plist
, Lisp_Object list
, INTERVAL i
, Lisp_Object object
)
463 bool changed
= false;
465 /* True means tail1 is a plist, otherwise it is a list. */
466 bool use_plist
= ! NILP (plist
);
467 Lisp_Object tail1
= use_plist
? plist
: list
;
469 Lisp_Object current_plist
= i
->plist
;
471 /* Go through each element of LIST or PLIST. */
472 while (CONSP (tail1
))
474 Lisp_Object sym
= XCAR (tail1
);
476 /* First, remove the symbol if it's at the head of the list */
477 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
479 if (BUFFERP (object
))
480 record_property_change (i
->position
, LENGTH (i
),
481 sym
, XCAR (XCDR (current_plist
)),
484 current_plist
= XCDR (XCDR (current_plist
));
488 /* Go through I's plist, looking for SYM. */
489 Lisp_Object tail2
= current_plist
;
490 while (! NILP (tail2
))
492 Lisp_Object
this = XCDR (XCDR (tail2
));
493 if (CONSP (this) && EQ (sym
, XCAR (this)))
495 if (BUFFERP (object
))
496 record_property_change (i
->position
, LENGTH (i
),
497 sym
, XCAR (XCDR (this)), object
);
499 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
505 /* Advance thru TAIL1 one way or the other. */
506 tail1
= XCDR (tail1
);
507 if (use_plist
&& CONSP (tail1
))
508 tail1
= XCDR (tail1
);
512 set_interval_plist (i
, current_plist
);
516 /* Returns the interval of POSITION in OBJECT.
517 POSITION is BEG-based. */
520 interval_of (ptrdiff_t position
, Lisp_Object object
)
526 XSETBUFFER (object
, current_buffer
);
527 else if (EQ (object
, Qt
))
530 CHECK_STRING_OR_BUFFER (object
);
532 if (BUFFERP (object
))
534 register struct buffer
*b
= XBUFFER (object
);
538 i
= buffer_intervals (b
);
543 end
= SCHARS (object
);
544 i
= string_intervals (object
);
547 if (!(beg
<= position
&& position
<= end
))
548 args_out_of_range (make_number (position
), make_number (position
));
549 if (beg
== end
|| !i
)
552 return find_interval (i
, position
);
555 DEFUN ("text-properties-at", Ftext_properties_at
,
556 Stext_properties_at
, 1, 2, 0,
557 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
558 If the optional second argument OBJECT is a buffer (or nil, which means
559 the current buffer), POSITION is a buffer position (integer or marker).
560 If OBJECT is a string, POSITION is a 0-based index into it.
561 If POSITION is at the end of OBJECT, the value is nil. */)
562 (Lisp_Object position
, Lisp_Object object
)
567 XSETBUFFER (object
, current_buffer
);
569 i
= validate_interval_range (object
, &position
, &position
, soft
);
572 /* If POSITION is at the end of the interval,
573 it means it's the end of OBJECT.
574 There are no properties at the very end,
575 since no character follows. */
576 if (XINT (position
) == LENGTH (i
) + i
->position
)
582 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
583 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
584 OBJECT should be a buffer or a string; if omitted or nil, it defaults
585 to the current buffer.
586 If POSITION is at the end of OBJECT, the value is nil. */)
587 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
589 return textget (Ftext_properties_at (position
, object
), prop
);
592 /* Return the value of char's property PROP, in OBJECT at POSITION.
593 OBJECT is optional and defaults to the current buffer.
594 If OVERLAY is non-0, then in the case that the returned property is from
595 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
596 returned in *OVERLAY.
597 If POSITION is at the end of OBJECT, the value is nil.
598 If OBJECT is a buffer, then overlay properties are considered as well as
600 If OBJECT is a window, then that window's buffer is used, but
601 window-specific overlays are considered only if they are associated
604 get_char_property_and_overlay (Lisp_Object position
, register Lisp_Object prop
, Lisp_Object object
, Lisp_Object
*overlay
)
606 struct window
*w
= 0;
608 CHECK_NUMBER_COERCE_MARKER (position
);
611 XSETBUFFER (object
, current_buffer
);
613 if (WINDOWP (object
))
615 CHECK_LIVE_WINDOW (object
);
616 w
= XWINDOW (object
);
617 object
= w
->contents
;
619 if (BUFFERP (object
))
622 Lisp_Object
*overlay_vec
;
623 struct buffer
*obuf
= current_buffer
;
625 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
626 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
627 xsignal1 (Qargs_out_of_range
, position
);
629 set_buffer_temp (XBUFFER (object
));
632 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, false);
633 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
635 set_buffer_temp (obuf
);
637 /* Now check the overlays in order of decreasing priority. */
638 while (--noverlays
>= 0)
640 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
644 /* Return the overlay we got the property from. */
645 *overlay
= overlay_vec
[noverlays
];
654 /* Indicate that the return value is not from an overlay. */
657 /* Not a buffer, or no appropriate overlay, so fall through to the
659 return Fget_text_property (position
, prop
, object
);
662 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
663 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
664 Both overlay properties and text properties are checked.
665 OBJECT is optional and defaults to the current buffer.
666 If POSITION is at the end of OBJECT, the value is nil.
667 If OBJECT is a buffer, then overlay properties are considered as well as
669 If OBJECT is a window, then that window's buffer is used, but window-specific
670 overlays are considered only if they are associated with OBJECT. */)
671 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
673 return get_char_property_and_overlay (position
, prop
, object
, 0);
676 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
677 Sget_char_property_and_overlay
, 2, 3, 0,
678 doc
: /* Like `get-char-property', but with extra overlay information.
679 The value is a cons cell. Its car is the return value of `get-char-property'
680 with the same arguments--that is, the value of POSITION's property
681 PROP in OBJECT. Its cdr is the overlay in which the property was
682 found, or nil, if it was found as a text property or not found at all.
684 OBJECT is optional and defaults to the current buffer. OBJECT may be
685 a string, a buffer or a window. For strings, the cdr of the return
686 value is always nil, since strings do not have overlays. If OBJECT is
687 a window, then that window's buffer is used, but window-specific
688 overlays are considered only if they are associated with OBJECT. If
689 POSITION is at the end of OBJECT, both car and cdr are nil. */)
690 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
694 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
695 return Fcons (val
, overlay
);
699 DEFUN ("next-char-property-change", Fnext_char_property_change
,
700 Snext_char_property_change
, 1, 2, 0,
701 doc
: /* Return the position of next text property or overlay change.
702 This scans characters forward in the current buffer from POSITION till
703 it finds a change in some text property, or the beginning or end of an
704 overlay, and returns the position of that.
705 If none is found, and LIMIT is nil or omitted, the function
708 If the optional second argument LIMIT is non-nil, the function doesn't
709 search past position LIMIT, and returns LIMIT if nothing is found
710 before LIMIT. LIMIT is a no-op if it is greater than (point-max). */)
711 (Lisp_Object position
, Lisp_Object limit
)
715 temp
= Fnext_overlay_change (position
);
718 CHECK_NUMBER_COERCE_MARKER (limit
);
719 if (XINT (limit
) < XINT (temp
))
722 return Fnext_property_change (position
, Qnil
, temp
);
725 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
726 Sprevious_char_property_change
, 1, 2, 0,
727 doc
: /* Return the position of previous text property or overlay change.
728 Scans characters backward in the current buffer from POSITION till it
729 finds a change in some text property, or the beginning or end of an
730 overlay, and returns the position of that.
731 If none is found, and LIMIT is nil or omitted, the function
734 If the optional second argument LIMIT is non-nil, the function doesn't
735 search before position LIMIT, and returns LIMIT if nothing is found
736 before LIMIT. LIMIT is a no-op if it is less than (point-min). */)
737 (Lisp_Object position
, Lisp_Object limit
)
741 temp
= Fprevious_overlay_change (position
);
744 CHECK_NUMBER_COERCE_MARKER (limit
);
745 if (XINT (limit
) > XINT (temp
))
748 return Fprevious_property_change (position
, Qnil
, temp
);
752 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
753 Snext_single_char_property_change
, 2, 4, 0,
754 doc
: /* Return the position of next text property or overlay change for a specific property.
755 Scans characters forward from POSITION till it finds
756 a change in the PROP property, then returns the position of the change.
757 If the optional third argument OBJECT is a buffer (or nil, which means
758 the current buffer), POSITION is a buffer position (integer or marker).
759 If OBJECT is a string, POSITION is a 0-based index into it.
761 In a string, scan runs to the end of the string, unless LIMIT is non-nil.
762 In a buffer, if LIMIT is nil or omitted, it runs to (point-max), and the
763 value cannot exceed that.
764 If the optional fourth argument LIMIT is non-nil, don't search
765 past position LIMIT; return LIMIT if nothing is found before LIMIT.
767 The property values are compared with `eq'.
768 If the property is constant all the way to the end of OBJECT, return the
769 last valid position in OBJECT. */)
770 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
772 if (STRINGP (object
))
774 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
778 position
= make_number (SCHARS (object
));
781 CHECK_NUMBER (limit
);
788 Lisp_Object initial_value
, value
;
789 ptrdiff_t count
= SPECPDL_INDEX ();
792 CHECK_BUFFER (object
);
794 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
796 record_unwind_current_buffer ();
797 Fset_buffer (object
);
800 CHECK_NUMBER_COERCE_MARKER (position
);
802 initial_value
= Fget_char_property (position
, prop
, object
);
805 XSETFASTINT (limit
, ZV
);
807 CHECK_NUMBER_COERCE_MARKER (limit
);
809 if (XFASTINT (position
) >= XFASTINT (limit
))
812 if (XFASTINT (position
) > ZV
)
813 XSETFASTINT (position
, ZV
);
818 position
= Fnext_char_property_change (position
, limit
);
819 if (XFASTINT (position
) >= XFASTINT (limit
))
825 value
= Fget_char_property (position
, prop
, object
);
826 if (!EQ (value
, initial_value
))
830 unbind_to (count
, Qnil
);
836 DEFUN ("previous-single-char-property-change",
837 Fprevious_single_char_property_change
,
838 Sprevious_single_char_property_change
, 2, 4, 0,
839 doc
: /* Return the position of previous text property or overlay change for a specific property.
840 Scans characters backward from POSITION till it finds
841 a change in the PROP property, then returns the position of the change.
842 If the optional third argument OBJECT is a buffer (or nil, which means
843 the current buffer), POSITION is a buffer position (integer or marker).
844 If OBJECT is a string, POSITION is a 0-based index into it.
846 In a string, scan runs to the start of the string, unless LIMIT is non-nil.
847 In a buffer, if LIMIT is nil or omitted, it runs to (point-min), and the
848 value cannot be less than that.
849 If the optional fourth argument LIMIT is non-nil, don't search back past
850 position LIMIT; return LIMIT if nothing is found before reaching LIMIT.
852 The property values are compared with `eq'.
853 If the property is constant all the way to the start of OBJECT, return the
854 first valid position in OBJECT. */)
855 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
857 if (STRINGP (object
))
859 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
863 position
= make_number (0);
866 CHECK_NUMBER (limit
);
873 ptrdiff_t count
= SPECPDL_INDEX ();
876 CHECK_BUFFER (object
);
878 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
880 record_unwind_current_buffer ();
881 Fset_buffer (object
);
884 CHECK_NUMBER_COERCE_MARKER (position
);
887 XSETFASTINT (limit
, BEGV
);
889 CHECK_NUMBER_COERCE_MARKER (limit
);
891 if (XFASTINT (position
) <= XFASTINT (limit
))
894 if (XFASTINT (position
) < BEGV
)
895 XSETFASTINT (position
, BEGV
);
899 Lisp_Object initial_value
900 = Fget_char_property (make_number (XFASTINT (position
) - 1),
905 position
= Fprevious_char_property_change (position
, limit
);
907 if (XFASTINT (position
) <= XFASTINT (limit
))
915 = Fget_char_property (make_number (XFASTINT (position
) - 1),
918 if (!EQ (value
, initial_value
))
924 unbind_to (count
, Qnil
);
930 DEFUN ("next-property-change", Fnext_property_change
,
931 Snext_property_change
, 1, 3, 0,
932 doc
: /* Return the position of next property change.
933 Scans characters forward from POSITION in OBJECT till it finds
934 a change in some text property, then returns the position of the change.
935 If the optional second argument OBJECT is a buffer (or nil, which means
936 the current buffer), POSITION is a buffer position (integer or marker).
937 If OBJECT is a string, POSITION is a 0-based index into it.
938 Return nil if LIMIT is nil or omitted, and the property is constant all
939 the way to the end of OBJECT; if the value is non-nil, it is a position
940 greater than POSITION, never equal.
942 If the optional third argument LIMIT is non-nil, don't search
943 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
944 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
946 register INTERVAL i
, next
;
949 XSETBUFFER (object
, current_buffer
);
951 if (!NILP (limit
) && !EQ (limit
, Qt
))
952 CHECK_NUMBER_COERCE_MARKER (limit
);
954 i
= validate_interval_range (object
, &position
, &position
, soft
);
956 /* If LIMIT is t, return start of next interval--don't
957 bother checking further intervals. */
963 next
= next_interval (i
);
966 XSETFASTINT (position
, (STRINGP (object
)
968 : BUF_ZV (XBUFFER (object
))));
970 XSETFASTINT (position
, next
->position
);
977 next
= next_interval (i
);
979 while (next
&& intervals_equal (i
, next
)
980 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
981 next
= next_interval (next
);
989 : BUF_ZV (XBUFFER (object
))))))
992 return make_number (next
->position
);
995 DEFUN ("next-single-property-change", Fnext_single_property_change
,
996 Snext_single_property_change
, 2, 4, 0,
997 doc
: /* Return the position of next property change for a specific property.
998 Scans characters forward from POSITION till it finds
999 a change in the PROP property, then returns the position of the change.
1000 If the optional third argument OBJECT is a buffer (or nil, which means
1001 the current buffer), POSITION is a buffer position (integer or marker).
1002 If OBJECT is a string, POSITION is a 0-based index into it.
1003 The property values are compared with `eq'.
1004 Return nil if LIMIT is nil or omitted, and the property is constant all
1005 the way to the end of OBJECT; if the value is non-nil, it is a position
1006 greater than POSITION, never equal.
1008 If the optional fourth argument LIMIT is non-nil, don't search
1009 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1010 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1012 register INTERVAL i
, next
;
1013 register Lisp_Object here_val
;
1016 XSETBUFFER (object
, current_buffer
);
1019 CHECK_NUMBER_COERCE_MARKER (limit
);
1021 i
= validate_interval_range (object
, &position
, &position
, soft
);
1025 here_val
= textget (i
->plist
, prop
);
1026 next
= next_interval (i
);
1028 && EQ (here_val
, textget (next
->plist
, prop
))
1029 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
1030 next
= next_interval (next
);
1034 >= (INTEGERP (limit
)
1038 : BUF_ZV (XBUFFER (object
))))))
1041 return make_number (next
->position
);
1044 DEFUN ("previous-property-change", Fprevious_property_change
,
1045 Sprevious_property_change
, 1, 3, 0,
1046 doc
: /* Return the position of previous property change.
1047 Scans characters backwards from POSITION in OBJECT till it finds
1048 a change in some text property, then returns the position of the change.
1049 If the optional second argument OBJECT is a buffer (or nil, which means
1050 the current buffer), POSITION is a buffer position (integer or marker).
1051 If OBJECT is a string, POSITION is a 0-based index into it.
1052 Return nil if LIMIT is nil or omitted, and the property is constant all
1053 the way to the start of OBJECT; if the value is non-nil, it is a position
1054 less than POSITION, never equal.
1056 If the optional third argument LIMIT is non-nil, don't search
1057 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1058 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
1060 register INTERVAL i
, previous
;
1063 XSETBUFFER (object
, current_buffer
);
1066 CHECK_NUMBER_COERCE_MARKER (limit
);
1068 i
= validate_interval_range (object
, &position
, &position
, soft
);
1072 /* Start with the interval containing the char before point. */
1073 if (i
->position
== XFASTINT (position
))
1074 i
= previous_interval (i
);
1076 previous
= previous_interval (i
);
1077 while (previous
&& intervals_equal (previous
, i
)
1079 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1080 previous
= previous_interval (previous
);
1083 || (previous
->position
+ LENGTH (previous
)
1084 <= (INTEGERP (limit
)
1086 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1089 return make_number (previous
->position
+ LENGTH (previous
));
1092 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1093 Sprevious_single_property_change
, 2, 4, 0,
1094 doc
: /* Return the position of previous property change for a specific property.
1095 Scans characters backward from POSITION till it finds
1096 a change in the PROP property, then returns the position of the change.
1097 If the optional third argument OBJECT is a buffer (or nil, which means
1098 the current buffer), POSITION is a buffer position (integer or marker).
1099 If OBJECT is a string, POSITION is a 0-based index into it.
1100 The property values are compared with `eq'.
1101 Return nil if LIMIT is nil or omitted, and the property is constant all
1102 the way to the start of OBJECT; if the value is non-nil, it is a position
1103 less than POSITION, never equal.
1105 If the optional fourth argument LIMIT is non-nil, don't search
1106 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1107 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1109 register INTERVAL i
, previous
;
1110 register Lisp_Object here_val
;
1113 XSETBUFFER (object
, current_buffer
);
1116 CHECK_NUMBER_COERCE_MARKER (limit
);
1118 i
= validate_interval_range (object
, &position
, &position
, soft
);
1120 /* Start with the interval containing the char before point. */
1121 if (i
&& i
->position
== XFASTINT (position
))
1122 i
= previous_interval (i
);
1127 here_val
= textget (i
->plist
, prop
);
1128 previous
= previous_interval (i
);
1130 && EQ (here_val
, textget (previous
->plist
, prop
))
1132 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1133 previous
= previous_interval (previous
);
1136 || (previous
->position
+ LENGTH (previous
)
1137 <= (INTEGERP (limit
)
1139 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1142 return make_number (previous
->position
+ LENGTH (previous
));
1145 /* Used by add-text-properties and add-face-text-property. */
1148 add_text_properties_1 (Lisp_Object start
, Lisp_Object end
,
1149 Lisp_Object properties
, Lisp_Object object
,
1150 enum property_set_type set_type
) {
1151 INTERVAL i
, unchanged
;
1153 bool modified
= false;
1154 bool first_time
= true;
1156 properties
= validate_plist (properties
);
1157 if (NILP (properties
))
1161 XSETBUFFER (object
, current_buffer
);
1164 i
= validate_interval_range (object
, &start
, &end
, hard
);
1169 len
= XINT (end
) - s
;
1171 /* If this interval already has the properties, we can skip it. */
1172 if (interval_has_all_properties (properties
, i
))
1174 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1181 i
= next_interval (i
);
1184 while (interval_has_all_properties (properties
, i
));
1186 else if (i
->position
!= s
)
1188 /* If we're not starting on an interval boundary, we have to
1189 split this interval. */
1191 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1192 copy_properties (unchanged
, i
);
1195 if (BUFFERP (object
) && first_time
)
1197 ptrdiff_t prev_total_length
= TOTAL_LENGTH (i
);
1198 ptrdiff_t prev_pos
= i
->position
;
1200 modify_text_properties (object
, start
, end
);
1201 /* If someone called us recursively as a side effect of
1202 modify_text_properties, and changed the intervals behind our back
1203 (could happen if lock_file, called by prepare_to_modify_buffer,
1204 triggers redisplay, and that calls add-text-properties again
1205 in the same buffer), we cannot continue with I, because its
1206 data changed. So we restart the interval analysis anew. */
1207 if (TOTAL_LENGTH (i
) != prev_total_length
1208 || i
->position
!= prev_pos
)
1215 /* We are at the beginning of interval I, with LEN chars to scan. */
1220 if (LENGTH (i
) >= len
)
1222 if (interval_has_all_properties (properties
, i
))
1224 if (BUFFERP (object
))
1225 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1226 XINT (end
) - XINT (start
));
1232 if (LENGTH (i
) == len
)
1234 add_properties (properties
, i
, object
, set_type
);
1235 if (BUFFERP (object
))
1236 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1237 XINT (end
) - XINT (start
));
1241 /* i doesn't have the properties, and goes past the change limit */
1243 i
= split_interval_left (unchanged
, len
);
1244 copy_properties (unchanged
, i
);
1245 add_properties (properties
, i
, object
, set_type
);
1246 if (BUFFERP (object
))
1247 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1248 XINT (end
) - XINT (start
));
1253 modified
|= add_properties (properties
, i
, object
, set_type
);
1254 i
= next_interval (i
);
1258 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1260 DEFUN ("add-text-properties", Fadd_text_properties
,
1261 Sadd_text_properties
, 3, 4, 0,
1262 doc
: /* Add properties to the text from START to END.
1263 The third argument PROPERTIES is a property list
1264 specifying the property values to add. If the optional fourth argument
1265 OBJECT is a buffer (or nil, which means the current buffer),
1266 START and END are buffer positions (integers or markers).
1267 If OBJECT is a string, START and END are 0-based indices into it.
1268 Return t if any property value actually changed, nil otherwise. */)
1269 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
,
1272 return add_text_properties_1 (start
, end
, properties
, object
,
1273 TEXT_PROPERTY_REPLACE
);
1276 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1278 DEFUN ("put-text-property", Fput_text_property
,
1279 Sput_text_property
, 4, 5, 0,
1280 doc
: /* Set one property of the text from START to END.
1281 The third and fourth arguments PROPERTY and VALUE
1282 specify the property to add.
1283 If the optional fifth argument OBJECT is a buffer (or nil, which means
1284 the current buffer), START and END are buffer positions (integers or
1285 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1286 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
,
1287 Lisp_Object value
, Lisp_Object object
)
1289 AUTO_LIST2 (properties
, property
, value
);
1290 Fadd_text_properties (start
, end
, properties
, object
);
1294 DEFUN ("set-text-properties", Fset_text_properties
,
1295 Sset_text_properties
, 3, 4, 0,
1296 doc
: /* Completely replace properties of text from START to END.
1297 The third argument PROPERTIES is the new property list.
1298 If the optional fourth argument OBJECT is a buffer (or nil, which means
1299 the current buffer), START and END are buffer positions (integers or
1300 markers). If OBJECT is a string, START and END are 0-based indices into it.
1301 If PROPERTIES is nil, the effect is to remove all properties from
1302 the designated part of OBJECT. */)
1303 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1305 return set_text_properties (start
, end
, properties
, object
, Qt
);
1309 DEFUN ("add-face-text-property", Fadd_face_text_property
,
1310 Sadd_face_text_property
, 3, 5, 0,
1311 doc
: /* Add the face property to the text from START to END.
1312 FACE specifies the face to add. It should be a valid value of the
1313 `face' property (typically a face name or a plist of face attributes
1316 If any text in the region already has a non-nil `face' property, those
1317 face(s) are retained. This is done by setting the `face' property to
1318 a list of faces, with FACE as the first element (by default) and the
1319 pre-existing faces as the remaining elements.
1321 If optional fourth argument APPEND is non-nil, append FACE to the end
1322 of the face list instead.
1324 If optional fifth argument OBJECT is a buffer (or nil, which means the
1325 current buffer), START and END are buffer positions (integers or
1326 markers). If OBJECT is a string, START and END are 0-based indices
1328 (Lisp_Object start
, Lisp_Object end
, Lisp_Object face
,
1329 Lisp_Object append
, Lisp_Object object
)
1331 AUTO_LIST2 (properties
, Qface
, face
);
1332 add_text_properties_1 (start
, end
, properties
, object
,
1334 ? TEXT_PROPERTY_PREPEND
1335 : TEXT_PROPERTY_APPEND
));
1339 /* Replace properties of text from START to END with new list of
1340 properties PROPERTIES. OBJECT is the buffer or string containing
1341 the text. OBJECT nil means use the current buffer.
1342 COHERENT_CHANGE_P nil means this is being called as an internal
1343 subroutine, rather than as a change primitive with checking of
1344 read-only, invoking change hooks, etc.. Value is nil if the
1345 function _detected_ that it did not replace any properties, non-nil
1349 set_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
,
1350 Lisp_Object object
, Lisp_Object coherent_change_p
)
1352 register INTERVAL i
;
1353 Lisp_Object ostart
, oend
;
1358 properties
= validate_plist (properties
);
1361 XSETBUFFER (object
, current_buffer
);
1363 /* If we want no properties for a whole string,
1364 get rid of its intervals. */
1365 if (NILP (properties
) && STRINGP (object
)
1366 && XFASTINT (start
) == 0
1367 && XFASTINT (end
) == SCHARS (object
))
1369 if (!string_intervals (object
))
1372 set_string_intervals (object
, NULL
);
1376 i
= validate_interval_range (object
, &start
, &end
, soft
);
1380 /* If buffer has no properties, and we want none, return now. */
1381 if (NILP (properties
))
1384 /* Restore the original START and END values
1385 because validate_interval_range increments them for strings. */
1389 i
= validate_interval_range (object
, &start
, &end
, hard
);
1390 /* This can return if start == end. */
1395 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1396 modify_text_properties (object
, start
, end
);
1398 set_text_properties_1 (start
, end
, properties
, object
, i
);
1400 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1401 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1402 XINT (end
) - XINT (start
));
1406 /* Replace properties of text from START to END with new list of
1407 properties PROPERTIES. OBJECT is the buffer or string containing
1408 the text. This does not obey any hooks.
1409 You should provide the interval that START is located in as I.
1410 START and END can be in any order. */
1413 set_text_properties_1 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
, INTERVAL i
)
1415 register INTERVAL prev_changed
= NULL
;
1416 register ptrdiff_t s
, len
;
1419 if (XINT (start
) < XINT (end
))
1422 len
= XINT (end
) - s
;
1424 else if (XINT (end
) < XINT (start
))
1427 len
= XINT (start
) - s
;
1434 if (i
->position
!= s
)
1437 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1439 if (LENGTH (i
) > len
)
1441 copy_properties (unchanged
, i
);
1442 i
= split_interval_left (i
, len
);
1443 set_properties (properties
, i
, object
);
1447 set_properties (properties
, i
, object
);
1449 if (LENGTH (i
) == len
)
1454 i
= next_interval (i
);
1457 /* We are starting at the beginning of an interval I. LEN is positive. */
1462 if (LENGTH (i
) >= len
)
1464 if (LENGTH (i
) > len
)
1465 i
= split_interval_left (i
, len
);
1467 /* We have to call set_properties even if we are going to
1468 merge the intervals, so as to make the undo records
1469 and cause redisplay to happen. */
1470 set_properties (properties
, i
, object
);
1472 merge_interval_left (i
);
1478 /* We have to call set_properties even if we are going to
1479 merge the intervals, so as to make the undo records
1480 and cause redisplay to happen. */
1481 set_properties (properties
, i
, object
);
1485 prev_changed
= i
= merge_interval_left (i
);
1487 i
= next_interval (i
);
1492 DEFUN ("remove-text-properties", Fremove_text_properties
,
1493 Sremove_text_properties
, 3, 4, 0,
1494 doc
: /* Remove some properties from text from START to END.
1495 The third argument PROPERTIES is a property list
1496 whose property names specify the properties to remove.
1497 \(The values stored in PROPERTIES are ignored.)
1498 If the optional fourth argument OBJECT is a buffer (or nil, which means
1499 the current buffer), START and END are buffer positions (integers or
1500 markers). If OBJECT is a string, START and END are 0-based indices into it.
1501 Return t if any property was actually removed, nil otherwise.
1503 Use `set-text-properties' if you want to remove all text properties. */)
1504 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1506 INTERVAL i
, unchanged
;
1508 bool modified
= false;
1509 bool first_time
= true;
1512 XSETBUFFER (object
, current_buffer
);
1515 i
= validate_interval_range (object
, &start
, &end
, soft
);
1520 len
= XINT (end
) - s
;
1522 /* If there are no properties on this entire interval, return. */
1523 if (! interval_has_some_properties (properties
, i
))
1525 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1532 i
= next_interval (i
);
1535 while (! interval_has_some_properties (properties
, i
));
1537 /* Split away the beginning of this interval; what we don't
1539 else if (i
->position
!= s
)
1542 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1543 copy_properties (unchanged
, i
);
1546 if (BUFFERP (object
) && first_time
)
1548 ptrdiff_t prev_total_length
= TOTAL_LENGTH (i
);
1549 ptrdiff_t prev_pos
= i
->position
;
1551 modify_text_properties (object
, start
, end
);
1552 /* If someone called us recursively as a side effect of
1553 modify_text_properties, and changed the intervals behind our back
1554 (could happen if lock_file, called by prepare_to_modify_buffer,
1555 triggers redisplay, and that calls add-text-properties again
1556 in the same buffer), we cannot continue with I, because its
1557 data changed. So we restart the interval analysis anew. */
1558 if (TOTAL_LENGTH (i
) != prev_total_length
1559 || i
->position
!= prev_pos
)
1566 /* We are at the beginning of an interval, with len to scan */
1571 if (LENGTH (i
) >= len
)
1573 if (! interval_has_some_properties (properties
, i
))
1576 if (BUFFERP (object
))
1577 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1578 XINT (end
) - XINT (start
));
1582 if (LENGTH (i
) == len
)
1584 remove_properties (properties
, Qnil
, i
, object
);
1585 if (BUFFERP (object
))
1586 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1587 XINT (end
) - XINT (start
));
1591 /* i has the properties, and goes past the change limit */
1593 i
= split_interval_left (i
, len
);
1594 copy_properties (unchanged
, i
);
1595 remove_properties (properties
, Qnil
, i
, object
);
1596 if (BUFFERP (object
))
1597 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1598 XINT (end
) - XINT (start
));
1603 modified
|= remove_properties (properties
, Qnil
, i
, object
);
1604 i
= next_interval (i
);
1608 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1609 Sremove_list_of_text_properties
, 3, 4, 0,
1610 doc
: /* Remove some properties from text from START to END.
1611 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1612 If the optional fourth argument OBJECT is a buffer (or nil, which means
1613 the current buffer), START and END are buffer positions (integers or
1614 markers). If OBJECT is a string, START and END are 0-based indices into it.
1615 Return t if any property was actually removed, nil otherwise. */)
1616 (Lisp_Object start
, Lisp_Object end
, Lisp_Object list_of_properties
, Lisp_Object object
)
1618 INTERVAL i
, unchanged
;
1620 bool modified
= false;
1621 Lisp_Object properties
;
1622 properties
= list_of_properties
;
1625 XSETBUFFER (object
, current_buffer
);
1627 i
= validate_interval_range (object
, &start
, &end
, soft
);
1632 len
= XINT (end
) - s
;
1634 /* If there are no properties on the interval, return. */
1635 if (! interval_has_some_properties_list (properties
, i
))
1637 ptrdiff_t got
= LENGTH (i
) - (s
- i
->position
);
1644 i
= next_interval (i
);
1647 while (! interval_has_some_properties_list (properties
, i
));
1649 /* Split away the beginning of this interval; what we don't
1651 else if (i
->position
!= s
)
1654 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1655 copy_properties (unchanged
, i
);
1658 /* We are at the beginning of an interval, with len to scan.
1659 The flag MODIFIED records if changes have been made.
1660 When object is a buffer, we must call modify_text_properties
1661 before changes are made and signal_after_change when we are done.
1662 Call modify_text_properties before calling remove_properties if !MODIFIED,
1663 and call signal_after_change before returning if MODIFIED. */
1668 if (LENGTH (i
) >= len
)
1670 if (! interval_has_some_properties_list (properties
, i
))
1674 if (BUFFERP (object
))
1675 signal_after_change (XINT (start
),
1676 XINT (end
) - XINT (start
),
1677 XINT (end
) - XINT (start
));
1683 else if (LENGTH (i
) == len
)
1685 if (!modified
&& BUFFERP (object
))
1686 modify_text_properties (object
, start
, end
);
1687 remove_properties (Qnil
, properties
, i
, object
);
1688 if (BUFFERP (object
))
1689 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1690 XINT (end
) - XINT (start
));
1694 { /* i has the properties, and goes past the change limit. */
1696 i
= split_interval_left (i
, len
);
1697 copy_properties (unchanged
, i
);
1698 if (!modified
&& BUFFERP (object
))
1699 modify_text_properties (object
, start
, end
);
1700 remove_properties (Qnil
, properties
, i
, object
);
1701 if (BUFFERP (object
))
1702 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1703 XINT (end
) - XINT (start
));
1707 if (interval_has_some_properties_list (properties
, i
))
1709 if (!modified
&& BUFFERP (object
))
1710 modify_text_properties (object
, start
, end
);
1711 remove_properties (Qnil
, properties
, i
, object
);
1715 i
= next_interval (i
);
1720 if (BUFFERP (object
))
1721 signal_after_change (XINT (start
),
1722 XINT (end
) - XINT (start
),
1723 XINT (end
) - XINT (start
));
1732 DEFUN ("text-property-any", Ftext_property_any
,
1733 Stext_property_any
, 4, 5, 0,
1734 doc
: /* Check text from START to END for property PROPERTY equaling VALUE.
1735 If so, return the position of the first character whose property PROPERTY
1736 is `eq' to VALUE. Otherwise return nil.
1737 If the optional fifth argument OBJECT is a buffer (or nil, which means
1738 the current buffer), START and END are buffer positions (integers or
1739 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1740 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1742 register INTERVAL i
;
1743 register ptrdiff_t e
, pos
;
1746 XSETBUFFER (object
, current_buffer
);
1747 i
= validate_interval_range (object
, &start
, &end
, soft
);
1749 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1754 if (i
->position
>= e
)
1756 if (EQ (textget (i
->plist
, property
), value
))
1759 if (pos
< XINT (start
))
1761 return make_number (pos
);
1763 i
= next_interval (i
);
1768 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1769 Stext_property_not_all
, 4, 5, 0,
1770 doc
: /* Check text from START to END for property PROPERTY not equaling VALUE.
1771 If so, return the position of the first character whose property PROPERTY
1772 is not `eq' to VALUE. Otherwise, return nil.
1773 If the optional fifth argument OBJECT is a buffer (or nil, which means
1774 the current buffer), START and END are buffer positions (integers or
1775 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1776 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1778 register INTERVAL i
;
1779 register ptrdiff_t s
, e
;
1782 XSETBUFFER (object
, current_buffer
);
1783 i
= validate_interval_range (object
, &start
, &end
, soft
);
1785 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1791 if (i
->position
>= e
)
1793 if (! EQ (textget (i
->plist
, property
), value
))
1795 if (i
->position
> s
)
1797 return make_number (s
);
1799 i
= next_interval (i
);
1805 /* Return the direction from which the text-property PROP would be
1806 inherited by any new text inserted at POS: 1 if it would be
1807 inherited from the char after POS, -1 if it would be inherited from
1808 the char before POS, and 0 if from neither.
1809 BUFFER can be either a buffer or nil (meaning current buffer). */
1812 text_property_stickiness (Lisp_Object prop
, Lisp_Object pos
, Lisp_Object buffer
)
1814 bool ignore_previous_character
;
1815 Lisp_Object prev_pos
= make_number (XINT (pos
) - 1);
1816 Lisp_Object front_sticky
;
1817 bool is_rear_sticky
= true, is_front_sticky
= false; /* defaults */
1818 Lisp_Object defalt
= Fassq (prop
, Vtext_property_default_nonsticky
);
1821 XSETBUFFER (buffer
, current_buffer
);
1823 ignore_previous_character
= XINT (pos
) <= BUF_BEGV (XBUFFER (buffer
));
1825 if (ignore_previous_character
|| (CONSP (defalt
) && !NILP (XCDR (defalt
))))
1826 is_rear_sticky
= false;
1829 Lisp_Object rear_non_sticky
1830 = Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1832 if (!NILP (CONSP (rear_non_sticky
)
1833 ? Fmemq (prop
, rear_non_sticky
)
1835 /* PROP is rear-non-sticky. */
1836 is_rear_sticky
= false;
1839 /* Consider following character. */
1840 /* This signals an arg-out-of-range error if pos is outside the
1841 buffer's accessible range. */
1842 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1844 if (EQ (front_sticky
, Qt
)
1845 || (CONSP (front_sticky
)
1846 && !NILP (Fmemq (prop
, front_sticky
))))
1847 /* PROP is inherited from after. */
1848 is_front_sticky
= true;
1850 /* Simple cases, where the properties are consistent. */
1851 if (is_rear_sticky
&& !is_front_sticky
)
1853 else if (!is_rear_sticky
&& is_front_sticky
)
1855 else if (!is_rear_sticky
&& !is_front_sticky
)
1858 /* The stickiness properties are inconsistent, so we have to
1859 disambiguate. Basically, rear-sticky wins, _except_ if the
1860 property that would be inherited has a value of nil, in which case
1861 front-sticky wins. */
1862 if (ignore_previous_character
1863 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1870 /* Copying properties between objects. */
1872 /* Add properties from START to END of SRC, starting at POS in DEST.
1873 SRC and DEST may each refer to strings or buffers.
1874 Optional sixth argument PROP causes only that property to be copied.
1875 Properties are copied to DEST as if by `add-text-properties'.
1876 Return t if any property value actually changed, nil otherwise. */
1878 /* Note this can GC when DEST is a buffer. */
1881 copy_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object src
,
1882 Lisp_Object pos
, Lisp_Object dest
, Lisp_Object prop
)
1888 ptrdiff_t s
, e
, e2
, p
, len
;
1889 bool modified
= false;
1891 i
= validate_interval_range (src
, &start
, &end
, soft
);
1895 CHECK_NUMBER_COERCE_MARKER (pos
);
1897 Lisp_Object dest_start
, dest_end
;
1899 e
= XINT (pos
) + (XINT (end
) - XINT (start
));
1900 if (MOST_POSITIVE_FIXNUM
< e
)
1901 args_out_of_range (pos
, end
);
1903 XSETFASTINT (dest_end
, e
);
1904 /* Apply this to a copy of pos; it will try to increment its arguments,
1905 which we don't want. */
1906 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1917 e2
= i
->position
+ LENGTH (i
);
1924 while (! NILP (plist
))
1926 if (EQ (Fcar (plist
), prop
))
1928 plist
= list2 (prop
, Fcar (Fcdr (plist
)));
1931 plist
= Fcdr (Fcdr (plist
));
1934 /* Must defer modifications to the interval tree in case
1935 src and dest refer to the same string or buffer. */
1936 stuff
= Fcons (list3 (make_number (p
), make_number (p
+ len
), plist
),
1939 i
= next_interval (i
);
1947 while (! NILP (stuff
))
1950 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1951 Fcar (Fcdr (Fcdr (res
))), dest
);
1954 stuff
= Fcdr (stuff
);
1957 return modified
? Qt
: Qnil
;
1961 /* Return a list representing the text properties of OBJECT between
1962 START and END. if PROP is non-nil, report only on that property.
1963 Each result list element has the form (S E PLIST), where S and E
1964 are positions in OBJECT and PLIST is a property list containing the
1965 text properties of OBJECT between S and E. Value is nil if OBJECT
1966 doesn't contain text properties between START and END. */
1969 text_property_list (Lisp_Object object
, Lisp_Object start
, Lisp_Object end
, Lisp_Object prop
)
1976 i
= validate_interval_range (object
, &start
, &end
, soft
);
1979 ptrdiff_t s
= XINT (start
);
1980 ptrdiff_t e
= XINT (end
);
1984 ptrdiff_t interval_end
, len
;
1987 interval_end
= i
->position
+ LENGTH (i
);
1988 if (interval_end
> e
)
1990 len
= interval_end
- s
;
1995 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1996 if (EQ (XCAR (plist
), prop
))
1998 plist
= list2 (prop
, Fcar (XCDR (plist
)));
2003 result
= Fcons (list3 (make_number (s
), make_number (s
+ len
),
2007 i
= next_interval (i
);
2018 /* Add text properties to OBJECT from LIST. LIST is a list of triples
2019 (START END PLIST), where START and END are positions and PLIST is a
2020 property list containing the text properties to add. Adjust START
2021 and END positions by DELTA before adding properties. */
2024 add_text_properties_from_list (Lisp_Object object
, Lisp_Object list
, Lisp_Object delta
)
2026 for (; CONSP (list
); list
= XCDR (list
))
2028 Lisp_Object item
, start
, end
, plist
;
2031 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
2032 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
2033 plist
= XCAR (XCDR (XCDR (item
)));
2035 Fadd_text_properties (start
, end
, plist
, object
);
2041 /* Modify end-points of ranges in LIST destructively, and return the
2042 new list. LIST is a list as returned from text_property_list.
2043 Discard properties that begin at or after NEW_END, and limit
2044 end-points to NEW_END. */
2047 extend_property_ranges (Lisp_Object list
, Lisp_Object new_end
)
2049 Lisp_Object prev
= Qnil
, head
= list
;
2050 ptrdiff_t max
= XINT (new_end
);
2052 for (; CONSP (list
); prev
= list
, list
= XCDR (list
))
2054 Lisp_Object item
, beg
, end
;
2058 end
= 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 (XINT (end
) > max
)
2070 /* The end-point is past the end of the new string. */
2071 XSETCAR (XCDR (item
), new_end
);
2079 /* Call the modification hook functions in LIST, each with START and END. */
2082 call_mod_hooks (Lisp_Object list
, Lisp_Object start
, Lisp_Object end
)
2084 while (!NILP (list
))
2086 call2 (Fcar (list
), start
, end
);
2091 /* Check for read-only intervals between character positions START ... END,
2092 in BUF, and signal an error if we find one.
2094 Then check for any modification hooks in the range.
2095 Create a list of all these hooks in lexicographic order,
2096 eliminating consecutive extra copies of the same hook. Then call
2097 those hooks in order, with START and END - 1 as arguments. */
2100 verify_interval_modification (struct buffer
*buf
,
2101 ptrdiff_t start
, ptrdiff_t end
)
2103 INTERVAL intervals
= buffer_intervals (buf
);
2106 Lisp_Object prev_mod_hooks
;
2107 Lisp_Object mod_hooks
;
2110 prev_mod_hooks
= Qnil
;
2113 interval_insert_behind_hooks
= Qnil
;
2114 interval_insert_in_front_hooks
= Qnil
;
2121 ptrdiff_t temp
= start
;
2126 /* For an insert operation, check the two chars around the position. */
2129 INTERVAL prev
= NULL
;
2130 Lisp_Object before
, after
;
2132 /* Set I to the interval containing the char after START,
2133 and PREV to the interval containing the char before START.
2134 Either one may be null. They may be equal. */
2135 i
= find_interval (intervals
, start
);
2137 if (start
== BUF_BEGV (buf
))
2139 else if (i
->position
== start
)
2140 prev
= previous_interval (i
);
2141 else if (i
->position
< start
)
2143 if (start
== BUF_ZV (buf
))
2146 /* If Vinhibit_read_only is set and is not a list, we can
2147 skip the read_only checks. */
2148 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2150 /* If I and PREV differ we need to check for the read-only
2151 property together with its stickiness. If either I or
2152 PREV are 0, this check is all we need.
2153 We have to take special care, since read-only may be
2154 indirectly defined via the category property. */
2159 after
= textget (i
->plist
, Qread_only
);
2161 /* If interval I is read-only and read-only is
2162 front-sticky, inhibit insertion.
2163 Check for read-only as well as category. */
2165 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2169 tem
= textget (i
->plist
, Qfront_sticky
);
2170 if (TMEM (Qread_only
, tem
)
2171 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2172 && TMEM (Qcategory
, tem
)))
2173 text_read_only (after
);
2179 before
= textget (prev
->plist
, Qread_only
);
2181 /* If interval PREV is read-only and read-only isn't
2182 rear-nonsticky, inhibit insertion.
2183 Check for read-only as well as category. */
2185 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2189 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2190 if (! TMEM (Qread_only
, tem
)
2191 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2192 || ! TMEM (Qcategory
, tem
)))
2193 text_read_only (before
);
2199 after
= textget (i
->plist
, Qread_only
);
2201 /* If interval I is read-only and read-only is
2202 front-sticky, inhibit insertion.
2203 Check for read-only as well as category. */
2204 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2208 tem
= textget (i
->plist
, Qfront_sticky
);
2209 if (TMEM (Qread_only
, tem
)
2210 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2211 && TMEM (Qcategory
, tem
)))
2212 text_read_only (after
);
2214 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2215 if (! TMEM (Qread_only
, tem
)
2216 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2217 || ! TMEM (Qcategory
, tem
)))
2218 text_read_only (after
);
2223 /* Run both insert hooks (just once if they're the same). */
2225 interval_insert_behind_hooks
2226 = textget (prev
->plist
, Qinsert_behind_hooks
);
2228 interval_insert_in_front_hooks
2229 = textget (i
->plist
, Qinsert_in_front_hooks
);
2233 /* Loop over intervals on or next to START...END,
2234 collecting their hooks. */
2236 i
= find_interval (intervals
, start
);
2239 if (! INTERVAL_WRITABLE_P (i
))
2240 text_read_only (textget (i
->plist
, Qread_only
));
2242 if (!inhibit_modification_hooks
)
2244 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2245 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2247 hooks
= Fcons (mod_hooks
, hooks
);
2248 prev_mod_hooks
= mod_hooks
;
2252 if (i
->position
+ LENGTH (i
) < end
2253 && (!NILP (BVAR (current_buffer
, read_only
))
2254 && NILP (Vinhibit_read_only
)))
2255 xsignal1 (Qbuffer_read_only
, Fcurrent_buffer ());
2257 i
= next_interval (i
);
2259 /* Keep going thru the interval containing the char before END. */
2260 while (i
&& i
->position
< end
);
2262 if (!inhibit_modification_hooks
)
2264 hooks
= Fnreverse (hooks
);
2265 while (! EQ (hooks
, Qnil
))
2267 call_mod_hooks (Fcar (hooks
), make_number (start
),
2269 hooks
= Fcdr (hooks
);
2275 /* Run the interval hooks for an insertion on character range START ... END.
2276 verify_interval_modification chose which hooks to run;
2277 this function is called after the insertion happens
2278 so it can indicate the range of inserted text. */
2281 report_interval_modification (Lisp_Object start
, Lisp_Object end
)
2283 if (! NILP (interval_insert_behind_hooks
))
2284 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2285 if (! NILP (interval_insert_in_front_hooks
)
2286 && ! EQ (interval_insert_in_front_hooks
,
2287 interval_insert_behind_hooks
))
2288 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2292 syms_of_textprop (void)
2294 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties
,
2295 doc
: /* Property-list used as default values.
2296 The value of a property in this list is seen as the value for every
2297 character that does not have its own value for that property. */);
2298 Vdefault_text_properties
= Qnil
;
2300 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist
,
2301 doc
: /* Alist of alternative properties for properties without a value.
2302 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2303 If a piece of text has no direct value for a particular property, then
2304 this alist is consulted. If that property appears in the alist, then
2305 the first non-nil value from the associated alternative properties is
2307 Vchar_property_alias_alist
= Qnil
;
2309 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks
,
2310 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2311 This also inhibits the use of the `intangible' text property.
2313 This variable is obsolete since Emacs-25.1. Use `cursor-intangible-mode'
2314 or `cursor-sensor-mode' instead. */);
2315 /* FIXME: We should make-obsolete-variable, but that signals too many
2316 warnings in code which does (let ((inhibit-point-motion-hooks t)) ...)
2317 Ideally, make-obsolete-variable should let us specify that only the nil
2318 value is obsolete, but that requires too many changes in bytecomp.el,
2319 so for now we'll keep it "obsolete via the docstring". */
2320 Vinhibit_point_motion_hooks
= Qt
;
2322 DEFVAR_LISP ("text-property-default-nonsticky",
2323 Vtext_property_default_nonsticky
,
2324 doc
: /* Alist of properties vs the corresponding non-stickiness.
2325 Each element has the form (PROPERTY . NONSTICKINESS).
2327 If a character in a buffer has PROPERTY, new text inserted adjacent to
2328 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2329 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2330 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2331 /* Text properties `syntax-table'and `display' should be nonsticky
2333 Vtext_property_default_nonsticky
2334 = list2 (Fcons (Qsyntax_table
, Qt
), Fcons (Qdisplay
, Qt
));
2336 staticpro (&interval_insert_behind_hooks
);
2337 staticpro (&interval_insert_in_front_hooks
);
2338 interval_insert_behind_hooks
= Qnil
;
2339 interval_insert_in_front_hooks
= Qnil
;
2342 /* Common attributes one might give text. */
2344 DEFSYM (Qfont
, "font");
2345 DEFSYM (Qface
, "face");
2346 DEFSYM (Qread_only
, "read-only");
2347 DEFSYM (Qinvisible
, "invisible");
2348 DEFSYM (Qintangible
, "intangible");
2349 DEFSYM (Qcategory
, "category");
2350 DEFSYM (Qlocal_map
, "local-map");
2351 DEFSYM (Qfront_sticky
, "front-sticky");
2352 DEFSYM (Qrear_nonsticky
, "rear-nonsticky");
2353 DEFSYM (Qmouse_face
, "mouse-face");
2354 DEFSYM (Qminibuffer_prompt
, "minibuffer-prompt");
2356 /* Properties that text might use to specify certain actions. */
2358 DEFSYM (Qpoint_left
, "point-left");
2359 DEFSYM (Qpoint_entered
, "point-entered");
2361 defsubr (&Stext_properties_at
);
2362 defsubr (&Sget_text_property
);
2363 defsubr (&Sget_char_property
);
2364 defsubr (&Sget_char_property_and_overlay
);
2365 defsubr (&Snext_char_property_change
);
2366 defsubr (&Sprevious_char_property_change
);
2367 defsubr (&Snext_single_char_property_change
);
2368 defsubr (&Sprevious_single_char_property_change
);
2369 defsubr (&Snext_property_change
);
2370 defsubr (&Snext_single_property_change
);
2371 defsubr (&Sprevious_property_change
);
2372 defsubr (&Sprevious_single_property_change
);
2373 defsubr (&Sadd_text_properties
);
2374 defsubr (&Sput_text_property
);
2375 defsubr (&Sset_text_properties
);
2376 defsubr (&Sadd_face_text_property
);
2377 defsubr (&Sremove_text_properties
);
2378 defsubr (&Sremove_list_of_text_properties
);
2379 defsubr (&Stext_property_any
);
2380 defsubr (&Stext_property_not_all
);