1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2012 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
22 #include "intervals.h"
23 #include "character.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 static Lisp_Object Qmouse_left
;
48 static Lisp_Object Qmouse_entered
;
49 Lisp_Object Qpoint_left
;
50 Lisp_Object Qpoint_entered
;
51 Lisp_Object Qcategory
;
52 Lisp_Object Qlocal_map
;
54 /* Visual properties text (including strings) may have. */
55 static Lisp_Object Qforeground
, Qbackground
, Qunderline
;
57 static Lisp_Object Qstipple
;
58 Lisp_Object Qinvisible
, Qintangible
, Qmouse_face
;
59 static Lisp_Object Qread_only
;
60 Lisp_Object Qminibuffer_prompt
;
62 /* Sticky properties */
63 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
65 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
66 the o1's cdr. Otherwise, return zero. This is handy for
68 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70 /* verify_interval_modification saves insertion hooks here
71 to be run later by report_interval_modification. */
72 static Lisp_Object interval_insert_behind_hooks
;
73 static Lisp_Object interval_insert_in_front_hooks
;
76 /* Signal a `text-read-only' error. This function makes it easier
77 to capture that error in GDB by putting a breakpoint on it. */
80 text_read_only (Lisp_Object propval
)
82 if (STRINGP (propval
))
83 xsignal1 (Qtext_read_only
, propval
);
85 xsignal0 (Qtext_read_only
);
90 /* Extract the interval at the position pointed to by BEGIN from
91 OBJECT, a string or buffer. Additionally, check that the positions
92 pointed to by BEGIN and END are within the bounds of OBJECT, and
93 reverse them if *BEGIN is greater than *END. The objects pointed
94 to by BEGIN and END may be integers or markers; if the latter, they
95 are coerced to integers.
97 When OBJECT is a string, we increment *BEGIN and *END
98 to make them origin-one.
100 Note that buffer points don't correspond to interval indices.
101 For example, point-max is 1 greater than the index of the last
102 character. This difference is handled in the caller, which uses
103 the validated points to determine a length, and operates on that.
104 Exceptions are Ftext_properties_at, Fnext_property_change, and
105 Fprevious_property_change which call this function with BEGIN == END.
106 Handle this case specially.
108 If FORCE is soft (0), it's OK to return NULL. Otherwise,
109 create an interval tree for OBJECT if one doesn't exist, provided
110 the object actually contains text. In the current design, if there
111 is no text, there can be no text properties. */
117 validate_interval_range (Lisp_Object object
, Lisp_Object
*begin
, Lisp_Object
*end
, int force
)
122 CHECK_STRING_OR_BUFFER (object
);
123 CHECK_NUMBER_COERCE_MARKER (*begin
);
124 CHECK_NUMBER_COERCE_MARKER (*end
);
126 /* If we are asked for a point, but from a subr which operates
127 on a range, then return nothing. */
128 if (EQ (*begin
, *end
) && begin
!= end
)
131 if (XINT (*begin
) > XINT (*end
))
139 if (BUFFERP (object
))
141 register struct buffer
*b
= XBUFFER (object
);
143 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
144 && XINT (*end
) <= BUF_ZV (b
)))
145 args_out_of_range (*begin
, *end
);
146 i
= buffer_intervals (b
);
148 /* If there's no text, there are no properties. */
149 if (BUF_BEGV (b
) == BUF_ZV (b
))
152 searchpos
= XINT (*begin
);
156 ptrdiff_t len
= SCHARS (object
);
158 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
159 && XINT (*end
) <= len
))
160 args_out_of_range (*begin
, *end
);
161 XSETFASTINT (*begin
, XFASTINT (*begin
));
163 XSETFASTINT (*end
, XFASTINT (*end
));
164 i
= string_intervals (object
);
169 searchpos
= XINT (*begin
);
173 return (force
? create_root_interval (object
) : i
);
175 return find_interval (i
, searchpos
);
178 /* Validate LIST as a property list. If LIST is not a list, then
179 make one consisting of (LIST nil). Otherwise, verify that LIST
180 is even numbered and thus suitable as a plist. */
183 validate_plist (Lisp_Object list
)
191 register Lisp_Object tail
;
192 for (i
= 0, tail
= list
; CONSP (tail
); i
++)
198 error ("Odd length text property list");
202 return Fcons (list
, Fcons (Qnil
, Qnil
));
205 /* Return nonzero if interval I has all the properties,
206 with the same values, of list PLIST. */
209 interval_has_all_properties (Lisp_Object plist
, INTERVAL i
)
211 register Lisp_Object tail1
, tail2
, sym1
;
214 /* Go through each element of PLIST. */
215 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
220 /* Go through I's plist, looking for sym1 */
221 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
222 if (EQ (sym1
, XCAR (tail2
)))
224 /* Found the same property on both lists. If the
225 values are unequal, return zero. */
226 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
229 /* Property has same value on both lists; go to next one. */
241 /* Return nonzero if the plist of interval I has any of the
242 properties of PLIST, regardless of their values. */
245 interval_has_some_properties (Lisp_Object plist
, INTERVAL i
)
247 register Lisp_Object tail1
, tail2
, sym
;
249 /* Go through each element of PLIST. */
250 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
254 /* Go through i's plist, looking for tail1 */
255 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
256 if (EQ (sym
, XCAR (tail2
)))
263 /* Return nonzero if the plist of interval I has any of the
264 property names in LIST, regardless of their values. */
267 interval_has_some_properties_list (Lisp_Object list
, INTERVAL i
)
269 register Lisp_Object tail1
, tail2
, sym
;
271 /* Go through each element of LIST. */
272 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
276 /* Go through i's plist, looking for tail1 */
277 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
278 if (EQ (sym
, XCAR (tail2
)))
285 /* Changing the plists of individual intervals. */
287 /* Return the value of PROP in property-list PLIST, or Qunbound if it
290 property_value (Lisp_Object plist
, Lisp_Object prop
)
294 while (PLIST_ELT_P (plist
, value
))
295 if (EQ (XCAR (plist
), prop
))
298 plist
= XCDR (value
);
303 /* Set the properties of INTERVAL to PROPERTIES,
304 and record undo info for the previous values.
305 OBJECT is the string or buffer that INTERVAL belongs to. */
308 set_properties (Lisp_Object properties
, INTERVAL interval
, Lisp_Object object
)
310 Lisp_Object sym
, value
;
312 if (BUFFERP (object
))
314 /* For each property in the old plist which is missing from PROPERTIES,
315 or has a different value in PROPERTIES, make an undo record. */
316 for (sym
= interval
->plist
;
317 PLIST_ELT_P (sym
, value
);
319 if (! EQ (property_value (properties
, XCAR (sym
)),
322 record_property_change (interval
->position
, LENGTH (interval
),
323 XCAR (sym
), XCAR (value
),
327 /* For each new property that has no value at all in the old plist,
328 make an undo record binding it to nil, so it will be removed. */
329 for (sym
= properties
;
330 PLIST_ELT_P (sym
, value
);
332 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
334 record_property_change (interval
->position
, LENGTH (interval
),
340 /* Store new properties. */
341 set_interval_plist (interval
, Fcopy_sequence (properties
));
344 /* Add the properties of PLIST to the interval I, or set
345 the value of I's property to the value of the property on PLIST
346 if they are different.
348 OBJECT should be the string or buffer the interval is in.
350 Return nonzero if this changes I (i.e., if any members of PLIST
351 are actually added to I's plist) */
354 add_properties (Lisp_Object plist
, INTERVAL i
, Lisp_Object object
)
356 Lisp_Object tail1
, tail2
, sym1
, val1
;
357 register int changed
= 0;
359 struct gcpro gcpro1
, gcpro2
, gcpro3
;
364 /* No need to protect OBJECT, because we can GC only in the case
365 where it is a buffer, and live buffers are always protected.
366 I and its plist are also protected, via OBJECT. */
367 GCPRO3 (tail1
, sym1
, val1
);
369 /* Go through each element of PLIST. */
370 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
373 val1
= Fcar (XCDR (tail1
));
376 /* Go through I's plist, looking for sym1 */
377 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
378 if (EQ (sym1
, XCAR (tail2
)))
380 /* No need to gcpro, because tail2 protects this
381 and it must be a cons cell (we get an error otherwise). */
382 register Lisp_Object this_cdr
;
384 this_cdr
= XCDR (tail2
);
385 /* Found the property. Now check its value. */
388 /* The properties have the same value on both lists.
389 Continue to the next property. */
390 if (EQ (val1
, Fcar (this_cdr
)))
393 /* Record this change in the buffer, for undo purposes. */
394 if (BUFFERP (object
))
396 record_property_change (i
->position
, LENGTH (i
),
397 sym1
, Fcar (this_cdr
), object
);
400 /* I's property has a different value -- change it */
401 Fsetcar (this_cdr
, val1
);
408 /* Record this change in the buffer, for undo purposes. */
409 if (BUFFERP (object
))
411 record_property_change (i
->position
, LENGTH (i
),
414 set_interval_plist (i
, Fcons (sym1
, Fcons (val1
, i
->plist
)));
424 /* For any members of PLIST, or LIST,
425 which are properties of I, remove them from I's plist.
426 (If PLIST is non-nil, use that, otherwise use LIST.)
427 OBJECT is the string or buffer containing I. */
430 remove_properties (Lisp_Object plist
, Lisp_Object list
, INTERVAL i
, Lisp_Object object
)
432 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
433 register int changed
= 0;
435 /* Nonzero means tail1 is a plist, otherwise it is a list. */
438 current_plist
= i
->plist
;
441 tail1
= plist
, use_plist
= 1;
443 tail1
= list
, use_plist
= 0;
445 /* Go through each element of LIST or PLIST. */
446 while (CONSP (tail1
))
450 /* First, remove the symbol if it's at the head of the list */
451 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
453 if (BUFFERP (object
))
454 record_property_change (i
->position
, LENGTH (i
),
455 sym
, XCAR (XCDR (current_plist
)),
458 current_plist
= XCDR (XCDR (current_plist
));
462 /* Go through I's plist, looking for SYM. */
463 tail2
= current_plist
;
464 while (! NILP (tail2
))
466 register Lisp_Object
this;
467 this = XCDR (XCDR (tail2
));
468 if (CONSP (this) && EQ (sym
, XCAR (this)))
470 if (BUFFERP (object
))
471 record_property_change (i
->position
, LENGTH (i
),
472 sym
, XCAR (XCDR (this)), object
);
474 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
480 /* Advance thru TAIL1 one way or the other. */
481 tail1
= XCDR (tail1
);
482 if (use_plist
&& CONSP (tail1
))
483 tail1
= XCDR (tail1
);
487 set_interval_plist (i
, current_plist
);
491 /* Returns the interval of POSITION in OBJECT.
492 POSITION is BEG-based. */
495 interval_of (ptrdiff_t position
, Lisp_Object object
)
501 XSETBUFFER (object
, current_buffer
);
502 else if (EQ (object
, Qt
))
505 CHECK_STRING_OR_BUFFER (object
);
507 if (BUFFERP (object
))
509 register struct buffer
*b
= XBUFFER (object
);
513 i
= buffer_intervals (b
);
518 end
= SCHARS (object
);
519 i
= string_intervals (object
);
522 if (!(beg
<= position
&& position
<= end
))
523 args_out_of_range (make_number (position
), make_number (position
));
524 if (beg
== end
|| !i
)
527 return find_interval (i
, position
);
530 DEFUN ("text-properties-at", Ftext_properties_at
,
531 Stext_properties_at
, 1, 2, 0,
532 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
533 If the optional second argument OBJECT is a buffer (or nil, which means
534 the current buffer), POSITION is a buffer position (integer or marker).
535 If OBJECT is a string, POSITION is a 0-based index into it.
536 If POSITION is at the end of OBJECT, the value is nil. */)
537 (Lisp_Object position
, Lisp_Object object
)
542 XSETBUFFER (object
, current_buffer
);
544 i
= validate_interval_range (object
, &position
, &position
, soft
);
547 /* If POSITION is at the end of the interval,
548 it means it's the end of OBJECT.
549 There are no properties at the very end,
550 since no character follows. */
551 if (XINT (position
) == LENGTH (i
) + i
->position
)
557 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
558 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
559 OBJECT should be a buffer or a string; if omitted or nil, it defaults
560 to the current buffer.
561 If POSITION is at the end of OBJECT, the value is nil. */)
562 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
564 return textget (Ftext_properties_at (position
, object
), prop
);
567 /* Return the value of char's property PROP, in OBJECT at POSITION.
568 OBJECT is optional and defaults to the current buffer.
569 If OVERLAY is non-0, then in the case that the returned property is from
570 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
571 returned in *OVERLAY.
572 If POSITION is at the end of OBJECT, the value is nil.
573 If OBJECT is a buffer, then overlay properties are considered as well as
575 If OBJECT is a window, then that window's buffer is used, but
576 window-specific overlays are considered only if they are associated
579 get_char_property_and_overlay (Lisp_Object position
, register Lisp_Object prop
, Lisp_Object object
, Lisp_Object
*overlay
)
581 struct window
*w
= 0;
583 CHECK_NUMBER_COERCE_MARKER (position
);
586 XSETBUFFER (object
, current_buffer
);
588 if (WINDOWP (object
))
590 w
= XWINDOW (object
);
593 if (BUFFERP (object
))
596 Lisp_Object
*overlay_vec
;
597 struct buffer
*obuf
= current_buffer
;
599 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
600 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
601 xsignal1 (Qargs_out_of_range
, position
);
603 set_buffer_temp (XBUFFER (object
));
605 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
606 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
608 set_buffer_temp (obuf
);
610 /* Now check the overlays in order of decreasing priority. */
611 while (--noverlays
>= 0)
613 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
617 /* Return the overlay we got the property from. */
618 *overlay
= overlay_vec
[noverlays
];
625 /* Indicate that the return value is not from an overlay. */
628 /* Not a buffer, or no appropriate overlay, so fall through to the
630 return Fget_text_property (position
, prop
, object
);
633 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
634 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
635 Both overlay properties and text properties are checked.
636 OBJECT is optional and defaults to the current buffer.
637 If POSITION is at the end of OBJECT, the value is nil.
638 If OBJECT is a buffer, then overlay properties are considered as well as
640 If OBJECT is a window, then that window's buffer is used, but window-specific
641 overlays are considered only if they are associated with OBJECT. */)
642 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
644 return get_char_property_and_overlay (position
, prop
, object
, 0);
647 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
648 Sget_char_property_and_overlay
, 2, 3, 0,
649 doc
: /* Like `get-char-property', but with extra overlay information.
650 The value is a cons cell. Its car is the return value of `get-char-property'
651 with the same arguments--that is, the value of POSITION's property
652 PROP in OBJECT. Its cdr is the overlay in which the property was
653 found, or nil, if it was found as a text property or not found at all.
655 OBJECT is optional and defaults to the current buffer. OBJECT may be
656 a string, a buffer or a window. For strings, the cdr of the return
657 value is always nil, since strings do not have overlays. If OBJECT is
658 a window, then that window's buffer is used, but window-specific
659 overlays are considered only if they are associated with OBJECT. If
660 POSITION is at the end of OBJECT, both car and cdr are nil. */)
661 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
665 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
666 return Fcons (val
, overlay
);
670 DEFUN ("next-char-property-change", Fnext_char_property_change
,
671 Snext_char_property_change
, 1, 2, 0,
672 doc
: /* Return the position of next text property or overlay change.
673 This scans characters forward in the current buffer from POSITION till
674 it finds a change in some text property, or the beginning or end of an
675 overlay, and returns the position of that.
676 If none is found up to (point-max), the function returns (point-max).
678 If the optional second argument LIMIT is non-nil, don't search
679 past position LIMIT; return LIMIT if nothing is found before LIMIT.
680 LIMIT is a no-op if it is greater than (point-max). */)
681 (Lisp_Object position
, Lisp_Object limit
)
685 temp
= Fnext_overlay_change (position
);
688 CHECK_NUMBER_COERCE_MARKER (limit
);
689 if (XINT (limit
) < XINT (temp
))
692 return Fnext_property_change (position
, Qnil
, temp
);
695 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
696 Sprevious_char_property_change
, 1, 2, 0,
697 doc
: /* Return the position of previous text property or overlay change.
698 Scans characters backward in the current buffer from POSITION till it
699 finds a change in some text property, or the beginning or end of an
700 overlay, and returns the position of that.
701 If none is found since (point-min), the function returns (point-min).
703 If the optional second argument LIMIT is non-nil, don't search
704 past position LIMIT; return LIMIT if nothing is found before LIMIT.
705 LIMIT is a no-op if it is less than (point-min). */)
706 (Lisp_Object position
, Lisp_Object limit
)
710 temp
= Fprevious_overlay_change (position
);
713 CHECK_NUMBER_COERCE_MARKER (limit
);
714 if (XINT (limit
) > XINT (temp
))
717 return Fprevious_property_change (position
, Qnil
, temp
);
721 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
722 Snext_single_char_property_change
, 2, 4, 0,
723 doc
: /* Return the position of next text property or overlay change for a specific property.
724 Scans characters forward from POSITION till it finds
725 a change in the PROP property, then returns the position of the change.
726 If the optional third argument OBJECT is a buffer (or nil, which means
727 the current buffer), POSITION is a buffer position (integer or marker).
728 If OBJECT is a string, POSITION is a 0-based index into it.
730 In a string, scan runs to the end of the string.
731 In a buffer, it runs to (point-max), and the value cannot exceed that.
733 The property values are compared with `eq'.
734 If the property is constant all the way to the end of OBJECT, return the
735 last valid position in OBJECT.
736 If the optional fourth argument LIMIT is non-nil, don't search
737 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
738 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
740 if (STRINGP (object
))
742 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
746 position
= make_number (SCHARS (object
));
749 CHECK_NUMBER (limit
);
756 Lisp_Object initial_value
, value
;
757 ptrdiff_t count
= SPECPDL_INDEX ();
760 CHECK_BUFFER (object
);
762 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
764 record_unwind_current_buffer ();
765 Fset_buffer (object
);
768 CHECK_NUMBER_COERCE_MARKER (position
);
770 initial_value
= Fget_char_property (position
, prop
, object
);
773 XSETFASTINT (limit
, ZV
);
775 CHECK_NUMBER_COERCE_MARKER (limit
);
777 if (XFASTINT (position
) >= XFASTINT (limit
))
780 if (XFASTINT (position
) > ZV
)
781 XSETFASTINT (position
, ZV
);
786 position
= Fnext_char_property_change (position
, limit
);
787 if (XFASTINT (position
) >= XFASTINT (limit
))
793 value
= Fget_char_property (position
, prop
, object
);
794 if (!EQ (value
, initial_value
))
798 unbind_to (count
, Qnil
);
804 DEFUN ("previous-single-char-property-change",
805 Fprevious_single_char_property_change
,
806 Sprevious_single_char_property_change
, 2, 4, 0,
807 doc
: /* Return the position of previous text property or overlay change for a specific property.
808 Scans characters backward from POSITION till it finds
809 a change in the PROP property, then returns the position of the change.
810 If the optional third argument OBJECT is a buffer (or nil, which means
811 the current buffer), POSITION is a buffer position (integer or marker).
812 If OBJECT is a string, POSITION is a 0-based index into it.
814 In a string, scan runs to the start of the string.
815 In a buffer, it runs to (point-min), and the value cannot be less than that.
817 The property values are compared with `eq'.
818 If the property is constant all the way to the start of OBJECT, return the
819 first valid position in OBJECT.
820 If the optional fourth argument LIMIT is non-nil, don't search back past
821 position LIMIT; return LIMIT if nothing is found before reaching LIMIT. */)
822 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
824 if (STRINGP (object
))
826 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
830 position
= make_number (0);
833 CHECK_NUMBER (limit
);
840 ptrdiff_t count
= SPECPDL_INDEX ();
843 CHECK_BUFFER (object
);
845 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
847 record_unwind_current_buffer ();
848 Fset_buffer (object
);
851 CHECK_NUMBER_COERCE_MARKER (position
);
854 XSETFASTINT (limit
, BEGV
);
856 CHECK_NUMBER_COERCE_MARKER (limit
);
858 if (XFASTINT (position
) <= XFASTINT (limit
))
861 if (XFASTINT (position
) < BEGV
)
862 XSETFASTINT (position
, BEGV
);
866 Lisp_Object initial_value
867 = Fget_char_property (make_number (XFASTINT (position
) - 1),
872 position
= Fprevious_char_property_change (position
, limit
);
874 if (XFASTINT (position
) <= XFASTINT (limit
))
882 = Fget_char_property (make_number (XFASTINT (position
) - 1),
885 if (!EQ (value
, initial_value
))
891 unbind_to (count
, Qnil
);
897 DEFUN ("next-property-change", Fnext_property_change
,
898 Snext_property_change
, 1, 3, 0,
899 doc
: /* Return the position of next property change.
900 Scans characters forward from POSITION in OBJECT till it finds
901 a change in some text property, then returns the position of the change.
902 If the optional second argument OBJECT is a buffer (or nil, which means
903 the current buffer), POSITION is a buffer position (integer or marker).
904 If OBJECT is a string, POSITION is a 0-based index into it.
905 Return nil if the property is constant all the way to the end of OBJECT.
906 If the value is non-nil, it is a position greater than POSITION, never equal.
908 If the optional third argument LIMIT is non-nil, don't search
909 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
910 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
912 register INTERVAL i
, next
;
915 XSETBUFFER (object
, current_buffer
);
917 if (!NILP (limit
) && !EQ (limit
, Qt
))
918 CHECK_NUMBER_COERCE_MARKER (limit
);
920 i
= validate_interval_range (object
, &position
, &position
, soft
);
922 /* If LIMIT is t, return start of next interval--don't
923 bother checking further intervals. */
929 next
= next_interval (i
);
932 XSETFASTINT (position
, (STRINGP (object
)
934 : BUF_ZV (XBUFFER (object
))));
936 XSETFASTINT (position
, next
->position
);
943 next
= next_interval (i
);
945 while (next
&& intervals_equal (i
, next
)
946 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
947 next
= next_interval (next
);
955 : BUF_ZV (XBUFFER (object
))))))
958 return make_number (next
->position
);
961 DEFUN ("next-single-property-change", Fnext_single_property_change
,
962 Snext_single_property_change
, 2, 4, 0,
963 doc
: /* Return the position of next property change for a specific property.
964 Scans characters forward from POSITION till it finds
965 a change in the PROP property, then returns the position of the change.
966 If the optional third argument OBJECT is a buffer (or nil, which means
967 the current buffer), POSITION is a buffer position (integer or marker).
968 If OBJECT is a string, POSITION is a 0-based index into it.
969 The property values are compared with `eq'.
970 Return nil if the property is constant all the way to the end of OBJECT.
971 If the value is non-nil, it is a position greater than POSITION, never equal.
973 If the optional fourth argument LIMIT is non-nil, don't search
974 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
975 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
977 register INTERVAL i
, next
;
978 register Lisp_Object here_val
;
981 XSETBUFFER (object
, current_buffer
);
984 CHECK_NUMBER_COERCE_MARKER (limit
);
986 i
= validate_interval_range (object
, &position
, &position
, soft
);
990 here_val
= textget (i
->plist
, prop
);
991 next
= next_interval (i
);
993 && EQ (here_val
, textget (next
->plist
, prop
))
994 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
995 next
= next_interval (next
);
1003 : BUF_ZV (XBUFFER (object
))))))
1006 return make_number (next
->position
);
1009 DEFUN ("previous-property-change", Fprevious_property_change
,
1010 Sprevious_property_change
, 1, 3, 0,
1011 doc
: /* Return the position of previous property change.
1012 Scans characters backwards from POSITION in OBJECT till it finds
1013 a change in some text property, then returns the position of the change.
1014 If the optional second argument OBJECT is a buffer (or nil, which means
1015 the current buffer), POSITION is a buffer position (integer or marker).
1016 If OBJECT is a string, POSITION is a 0-based index into it.
1017 Return nil if the property is constant all the way to the start of OBJECT.
1018 If the value is non-nil, it is a position less than POSITION, never equal.
1020 If the optional third argument LIMIT is non-nil, don't search
1021 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1022 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
1024 register INTERVAL i
, previous
;
1027 XSETBUFFER (object
, current_buffer
);
1030 CHECK_NUMBER_COERCE_MARKER (limit
);
1032 i
= validate_interval_range (object
, &position
, &position
, soft
);
1036 /* Start with the interval containing the char before point. */
1037 if (i
->position
== XFASTINT (position
))
1038 i
= previous_interval (i
);
1040 previous
= previous_interval (i
);
1041 while (previous
&& intervals_equal (previous
, i
)
1043 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1044 previous
= previous_interval (previous
);
1047 || (previous
->position
+ LENGTH (previous
)
1048 <= (INTEGERP (limit
)
1050 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1053 return make_number (previous
->position
+ LENGTH (previous
));
1056 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1057 Sprevious_single_property_change
, 2, 4, 0,
1058 doc
: /* Return the position of previous property change for a specific property.
1059 Scans characters backward from POSITION till it finds
1060 a change in the PROP property, then returns the position of the change.
1061 If the optional third argument OBJECT is a buffer (or nil, which means
1062 the current buffer), POSITION is a buffer position (integer or marker).
1063 If OBJECT is a string, POSITION is a 0-based index into it.
1064 The property values are compared with `eq'.
1065 Return nil if the property is constant all the way to the start of OBJECT.
1066 If the value is non-nil, it is a position less than POSITION, never equal.
1068 If the optional fourth argument LIMIT is non-nil, don't search
1069 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1070 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1072 register INTERVAL i
, previous
;
1073 register Lisp_Object here_val
;
1076 XSETBUFFER (object
, current_buffer
);
1079 CHECK_NUMBER_COERCE_MARKER (limit
);
1081 i
= validate_interval_range (object
, &position
, &position
, soft
);
1083 /* Start with the interval containing the char before point. */
1084 if (i
&& i
->position
== XFASTINT (position
))
1085 i
= previous_interval (i
);
1090 here_val
= textget (i
->plist
, prop
);
1091 previous
= previous_interval (i
);
1093 && EQ (here_val
, textget (previous
->plist
, prop
))
1095 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1096 previous
= previous_interval (previous
);
1099 || (previous
->position
+ LENGTH (previous
)
1100 <= (INTEGERP (limit
)
1102 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1105 return make_number (previous
->position
+ LENGTH (previous
));
1108 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1110 DEFUN ("add-text-properties", Fadd_text_properties
,
1111 Sadd_text_properties
, 3, 4, 0,
1112 doc
: /* Add properties to the text from START to END.
1113 The third argument PROPERTIES is a property list
1114 specifying the property values to add. If the optional fourth argument
1115 OBJECT is a buffer (or nil, which means the current buffer),
1116 START and END are buffer positions (integers or markers).
1117 If OBJECT is a string, START and END are 0-based indices into it.
1118 Return t if any property value actually changed, nil otherwise. */)
1119 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1121 register INTERVAL i
, unchanged
;
1122 register ptrdiff_t s
, len
;
1123 register int modified
= 0;
1124 struct gcpro gcpro1
;
1126 properties
= validate_plist (properties
);
1127 if (NILP (properties
))
1131 XSETBUFFER (object
, current_buffer
);
1133 i
= validate_interval_range (object
, &start
, &end
, hard
);
1138 len
= XINT (end
) - s
;
1140 /* No need to protect OBJECT, because we GC only if it's a buffer,
1141 and live buffers are always protected. */
1142 GCPRO1 (properties
);
1144 /* If we're not starting on an interval boundary, we have to
1145 split this interval. */
1146 if (i
->position
!= s
)
1148 /* If this interval already has the properties, we can
1150 if (interval_has_all_properties (properties
, i
))
1152 ptrdiff_t got
= (LENGTH (i
) - (s
- i
->position
));
1154 RETURN_UNGCPRO (Qnil
);
1156 i
= next_interval (i
);
1161 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1162 copy_properties (unchanged
, i
);
1166 if (BUFFERP (object
))
1167 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1169 /* We are at the beginning of interval I, with LEN chars to scan. */
1174 if (LENGTH (i
) >= len
)
1176 /* We can UNGCPRO safely here, because there will be just
1177 one more chance to gc, in the next call to add_properties,
1178 and after that we will not need PROPERTIES or OBJECT again. */
1181 if (interval_has_all_properties (properties
, i
))
1183 if (BUFFERP (object
))
1184 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1185 XINT (end
) - XINT (start
));
1187 return modified
? Qt
: Qnil
;
1190 if (LENGTH (i
) == len
)
1192 add_properties (properties
, i
, object
);
1193 if (BUFFERP (object
))
1194 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1195 XINT (end
) - XINT (start
));
1199 /* i doesn't have the properties, and goes past the change limit */
1201 i
= split_interval_left (unchanged
, len
);
1202 copy_properties (unchanged
, i
);
1203 add_properties (properties
, i
, object
);
1204 if (BUFFERP (object
))
1205 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1206 XINT (end
) - XINT (start
));
1211 modified
+= add_properties (properties
, i
, object
);
1212 i
= next_interval (i
);
1216 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1218 DEFUN ("put-text-property", Fput_text_property
,
1219 Sput_text_property
, 4, 5, 0,
1220 doc
: /* Set one property of the text from START to END.
1221 The third and fourth arguments PROPERTY and VALUE
1222 specify the property to add.
1223 If the optional fifth argument OBJECT is a buffer (or nil, which means
1224 the current buffer), START and END are buffer positions (integers or
1225 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1226 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1228 Fadd_text_properties (start
, end
,
1229 Fcons (property
, Fcons (value
, Qnil
)),
1234 DEFUN ("set-text-properties", Fset_text_properties
,
1235 Sset_text_properties
, 3, 4, 0,
1236 doc
: /* Completely replace properties of text from START to END.
1237 The third argument PROPERTIES is the new property list.
1238 If the optional fourth argument OBJECT is a buffer (or nil, which means
1239 the current buffer), START and END are buffer positions (integers or
1240 markers). If OBJECT is a string, START and END are 0-based indices into it.
1241 If PROPERTIES is nil, the effect is to remove all properties from
1242 the designated part of OBJECT. */)
1243 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1245 return set_text_properties (start
, end
, properties
, object
, Qt
);
1249 /* Replace properties of text from START to END with new list of
1250 properties PROPERTIES. OBJECT is the buffer or string containing
1251 the text. OBJECT nil means use the current buffer.
1252 COHERENT_CHANGE_P nil means this is being called as an internal
1253 subroutine, rather than as a change primitive with checking of
1254 read-only, invoking change hooks, etc.. Value is nil if the
1255 function _detected_ that it did not replace any properties, non-nil
1259 set_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
, Lisp_Object coherent_change_p
)
1261 register INTERVAL i
;
1262 Lisp_Object ostart
, oend
;
1267 properties
= validate_plist (properties
);
1270 XSETBUFFER (object
, current_buffer
);
1272 /* If we want no properties for a whole string,
1273 get rid of its intervals. */
1274 if (NILP (properties
) && STRINGP (object
)
1275 && XFASTINT (start
) == 0
1276 && XFASTINT (end
) == SCHARS (object
))
1278 if (!string_intervals (object
))
1281 set_string_intervals (object
, NULL
);
1285 i
= validate_interval_range (object
, &start
, &end
, soft
);
1289 /* If buffer has no properties, and we want none, return now. */
1290 if (NILP (properties
))
1293 /* Restore the original START and END values
1294 because validate_interval_range increments them for strings. */
1298 i
= validate_interval_range (object
, &start
, &end
, hard
);
1299 /* This can return if start == end. */
1304 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1305 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1307 set_text_properties_1 (start
, end
, properties
, object
, i
);
1309 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1310 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1311 XINT (end
) - XINT (start
));
1315 /* Replace properties of text from START to END with new list of
1316 properties PROPERTIES. BUFFER is the buffer containing
1317 the text. This does not obey any hooks.
1318 You can provide the interval that START is located in as I,
1319 or pass NULL for I and this function will find it.
1320 START and END can be in any order. */
1323 set_text_properties_1 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object buffer
, INTERVAL i
)
1325 register INTERVAL prev_changed
= NULL
;
1326 register ptrdiff_t s
, len
;
1329 if (XINT (start
) < XINT (end
))
1332 len
= XINT (end
) - s
;
1334 else if (XINT (end
) < XINT (start
))
1337 len
= XINT (start
) - s
;
1343 i
= find_interval (buffer_intervals (XBUFFER (buffer
)), s
);
1345 if (i
->position
!= s
)
1348 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1350 if (LENGTH (i
) > len
)
1352 copy_properties (unchanged
, i
);
1353 i
= split_interval_left (i
, len
);
1354 set_properties (properties
, i
, buffer
);
1358 set_properties (properties
, i
, buffer
);
1360 if (LENGTH (i
) == len
)
1365 i
= next_interval (i
);
1368 /* We are starting at the beginning of an interval I. LEN is positive. */
1373 if (LENGTH (i
) >= len
)
1375 if (LENGTH (i
) > len
)
1376 i
= split_interval_left (i
, len
);
1378 /* We have to call set_properties even if we are going to
1379 merge the intervals, so as to make the undo records
1380 and cause redisplay to happen. */
1381 set_properties (properties
, i
, buffer
);
1383 merge_interval_left (i
);
1389 /* We have to call set_properties even if we are going to
1390 merge the intervals, so as to make the undo records
1391 and cause redisplay to happen. */
1392 set_properties (properties
, i
, buffer
);
1396 prev_changed
= i
= merge_interval_left (i
);
1398 i
= next_interval (i
);
1403 DEFUN ("remove-text-properties", Fremove_text_properties
,
1404 Sremove_text_properties
, 3, 4, 0,
1405 doc
: /* Remove some properties from text from START to END.
1406 The third argument PROPERTIES is a property list
1407 whose property names specify the properties to remove.
1408 \(The values stored in PROPERTIES are ignored.)
1409 If the optional fourth argument OBJECT is a buffer (or nil, which means
1410 the current buffer), START and END are buffer positions (integers or
1411 markers). If OBJECT is a string, START and END are 0-based indices into it.
1412 Return t if any property was actually removed, nil otherwise.
1414 Use `set-text-properties' if you want to remove all text properties. */)
1415 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1417 register INTERVAL i
, unchanged
;
1418 register ptrdiff_t s
, len
;
1419 register int modified
= 0;
1422 XSETBUFFER (object
, current_buffer
);
1424 i
= validate_interval_range (object
, &start
, &end
, soft
);
1429 len
= XINT (end
) - s
;
1431 if (i
->position
!= s
)
1433 /* No properties on this first interval -- return if
1434 it covers the entire region. */
1435 if (! interval_has_some_properties (properties
, i
))
1437 ptrdiff_t got
= (LENGTH (i
) - (s
- i
->position
));
1441 i
= next_interval (i
);
1443 /* Split away the beginning of this interval; what we don't
1448 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1449 copy_properties (unchanged
, i
);
1453 if (BUFFERP (object
))
1454 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1456 /* We are at the beginning of an interval, with len to scan */
1461 if (LENGTH (i
) >= len
)
1463 if (! interval_has_some_properties (properties
, i
))
1464 return modified
? Qt
: Qnil
;
1466 if (LENGTH (i
) == len
)
1468 remove_properties (properties
, Qnil
, i
, object
);
1469 if (BUFFERP (object
))
1470 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1471 XINT (end
) - XINT (start
));
1475 /* i has the properties, and goes past the change limit */
1477 i
= split_interval_left (i
, len
);
1478 copy_properties (unchanged
, i
);
1479 remove_properties (properties
, Qnil
, i
, object
);
1480 if (BUFFERP (object
))
1481 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1482 XINT (end
) - XINT (start
));
1487 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1488 i
= next_interval (i
);
1492 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1493 Sremove_list_of_text_properties
, 3, 4, 0,
1494 doc
: /* Remove some properties from text from START to END.
1495 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1496 If the optional fourth argument OBJECT is a buffer (or nil, which means
1497 the current buffer), START and END are buffer positions (integers or
1498 markers). If OBJECT is a string, START and END are 0-based indices into it.
1499 Return t if any property was actually removed, nil otherwise. */)
1500 (Lisp_Object start
, Lisp_Object end
, Lisp_Object list_of_properties
, Lisp_Object object
)
1502 register INTERVAL i
, unchanged
;
1503 register ptrdiff_t s
, len
;
1504 register int modified
= 0;
1505 Lisp_Object properties
;
1506 properties
= list_of_properties
;
1509 XSETBUFFER (object
, current_buffer
);
1511 i
= validate_interval_range (object
, &start
, &end
, soft
);
1516 len
= XINT (end
) - s
;
1518 if (i
->position
!= s
)
1520 /* No properties on this first interval -- return if
1521 it covers the entire region. */
1522 if (! interval_has_some_properties_list (properties
, i
))
1524 ptrdiff_t got
= (LENGTH (i
) - (s
- i
->position
));
1528 i
= next_interval (i
);
1530 /* Split away the beginning of this interval; what we don't
1535 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1536 copy_properties (unchanged
, i
);
1540 /* We are at the beginning of an interval, with len to scan.
1541 The flag `modified' records if changes have been made.
1542 When object is a buffer, we must call modify_region before changes are
1543 made and signal_after_change when we are done.
1544 We call modify_region before calling remove_properties if modified == 0,
1545 and we call signal_after_change before returning if modified != 0. */
1550 if (LENGTH (i
) >= len
)
1552 if (! interval_has_some_properties_list (properties
, i
))
1556 if (BUFFERP (object
))
1557 signal_after_change (XINT (start
),
1558 XINT (end
) - XINT (start
),
1559 XINT (end
) - XINT (start
));
1565 else if (LENGTH (i
) == len
)
1567 if (!modified
&& BUFFERP (object
))
1568 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1569 remove_properties (Qnil
, properties
, i
, object
);
1570 if (BUFFERP (object
))
1571 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1572 XINT (end
) - XINT (start
));
1576 { /* i has the properties, and goes past the change limit. */
1578 i
= split_interval_left (i
, len
);
1579 copy_properties (unchanged
, i
);
1580 if (!modified
&& BUFFERP (object
))
1581 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1582 remove_properties (Qnil
, properties
, i
, object
);
1583 if (BUFFERP (object
))
1584 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1585 XINT (end
) - XINT (start
));
1589 if (interval_has_some_properties_list (properties
, i
))
1591 if (!modified
&& BUFFERP (object
))
1592 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1593 remove_properties (Qnil
, properties
, i
, object
);
1597 i
= next_interval (i
);
1601 DEFUN ("text-property-any", Ftext_property_any
,
1602 Stext_property_any
, 4, 5, 0,
1603 doc
: /* Check text from START to END for property PROPERTY equaling VALUE.
1604 If so, return the position of the first character whose property PROPERTY
1605 is `eq' to VALUE. Otherwise return nil.
1606 If the optional fifth argument OBJECT is a buffer (or nil, which means
1607 the current buffer), START and END are buffer positions (integers or
1608 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1609 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1611 register INTERVAL i
;
1612 register ptrdiff_t e
, pos
;
1615 XSETBUFFER (object
, current_buffer
);
1616 i
= validate_interval_range (object
, &start
, &end
, soft
);
1618 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1623 if (i
->position
>= e
)
1625 if (EQ (textget (i
->plist
, property
), value
))
1628 if (pos
< XINT (start
))
1630 return make_number (pos
);
1632 i
= next_interval (i
);
1637 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1638 Stext_property_not_all
, 4, 5, 0,
1639 doc
: /* Check text from START to END for property PROPERTY not equaling VALUE.
1640 If so, return the position of the first character whose property PROPERTY
1641 is not `eq' to VALUE. Otherwise, return nil.
1642 If the optional fifth argument OBJECT is a buffer (or nil, which means
1643 the current buffer), START and END are buffer positions (integers or
1644 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1645 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1647 register INTERVAL i
;
1648 register ptrdiff_t s
, e
;
1651 XSETBUFFER (object
, current_buffer
);
1652 i
= validate_interval_range (object
, &start
, &end
, soft
);
1654 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1660 if (i
->position
>= e
)
1662 if (! EQ (textget (i
->plist
, property
), value
))
1664 if (i
->position
> s
)
1666 return make_number (s
);
1668 i
= next_interval (i
);
1674 /* Return the direction from which the text-property PROP would be
1675 inherited by any new text inserted at POS: 1 if it would be
1676 inherited from the char after POS, -1 if it would be inherited from
1677 the char before POS, and 0 if from neither.
1678 BUFFER can be either a buffer or nil (meaning current buffer). */
1681 text_property_stickiness (Lisp_Object prop
, Lisp_Object pos
, Lisp_Object buffer
)
1683 Lisp_Object prev_pos
, front_sticky
;
1684 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1685 Lisp_Object defalt
= Fassq (prop
, Vtext_property_default_nonsticky
);
1688 XSETBUFFER (buffer
, current_buffer
);
1690 if (CONSP (defalt
) && !NILP (XCDR (defalt
)))
1693 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1694 /* Consider previous character. */
1696 Lisp_Object rear_non_sticky
;
1698 prev_pos
= make_number (XINT (pos
) - 1);
1699 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1701 if (!NILP (CONSP (rear_non_sticky
)
1702 ? Fmemq (prop
, rear_non_sticky
)
1704 /* PROP is rear-non-sticky. */
1710 /* Consider following character. */
1711 /* This signals an arg-out-of-range error if pos is outside the
1712 buffer's accessible range. */
1713 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1715 if (EQ (front_sticky
, Qt
)
1716 || (CONSP (front_sticky
)
1717 && !NILP (Fmemq (prop
, front_sticky
))))
1718 /* PROP is inherited from after. */
1719 is_front_sticky
= 1;
1721 /* Simple cases, where the properties are consistent. */
1722 if (is_rear_sticky
&& !is_front_sticky
)
1724 else if (!is_rear_sticky
&& is_front_sticky
)
1726 else if (!is_rear_sticky
&& !is_front_sticky
)
1729 /* The stickiness properties are inconsistent, so we have to
1730 disambiguate. Basically, rear-sticky wins, _except_ if the
1731 property that would be inherited has a value of nil, in which case
1732 front-sticky wins. */
1733 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1734 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1741 /* Copying properties between objects. */
1743 /* Add properties from START to END of SRC, starting at POS in DEST.
1744 SRC and DEST may each refer to strings or buffers.
1745 Optional sixth argument PROP causes only that property to be copied.
1746 Properties are copied to DEST as if by `add-text-properties'.
1747 Return t if any property value actually changed, nil otherwise. */
1749 /* Note this can GC when DEST is a buffer. */
1752 copy_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object src
, Lisp_Object pos
, Lisp_Object dest
, Lisp_Object prop
)
1758 ptrdiff_t s
, e
, e2
, p
, len
;
1760 struct gcpro gcpro1
, gcpro2
;
1762 i
= validate_interval_range (src
, &start
, &end
, soft
);
1766 CHECK_NUMBER_COERCE_MARKER (pos
);
1768 Lisp_Object dest_start
, dest_end
;
1770 e
= XINT (pos
) + (XINT (end
) - XINT (start
));
1771 if (MOST_POSITIVE_FIXNUM
< e
)
1772 args_out_of_range (pos
, end
);
1774 XSETFASTINT (dest_end
, e
);
1775 /* Apply this to a copy of pos; it will try to increment its arguments,
1776 which we don't want. */
1777 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1788 e2
= i
->position
+ LENGTH (i
);
1795 while (! NILP (plist
))
1797 if (EQ (Fcar (plist
), prop
))
1799 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1802 plist
= Fcdr (Fcdr (plist
));
1806 /* Must defer modifications to the interval tree in case src
1807 and dest refer to the same string or buffer. */
1808 stuff
= Fcons (Fcons (make_number (p
),
1809 Fcons (make_number (p
+ len
),
1810 Fcons (plist
, Qnil
))),
1814 i
= next_interval (i
);
1822 GCPRO2 (stuff
, dest
);
1824 while (! NILP (stuff
))
1827 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1828 Fcar (Fcdr (Fcdr (res
))), dest
);
1831 stuff
= Fcdr (stuff
);
1836 return modified
? Qt
: Qnil
;
1840 /* Return a list representing the text properties of OBJECT between
1841 START and END. if PROP is non-nil, report only on that property.
1842 Each result list element has the form (S E PLIST), where S and E
1843 are positions in OBJECT and PLIST is a property list containing the
1844 text properties of OBJECT between S and E. Value is nil if OBJECT
1845 doesn't contain text properties between START and END. */
1848 text_property_list (Lisp_Object object
, Lisp_Object start
, Lisp_Object end
, Lisp_Object prop
)
1855 i
= validate_interval_range (object
, &start
, &end
, soft
);
1858 ptrdiff_t s
= XINT (start
);
1859 ptrdiff_t e
= XINT (end
);
1863 ptrdiff_t interval_end
, len
;
1866 interval_end
= i
->position
+ LENGTH (i
);
1867 if (interval_end
> e
)
1869 len
= interval_end
- s
;
1874 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1875 if (EQ (XCAR (plist
), prop
))
1877 plist
= Fcons (prop
, Fcons (Fcar (XCDR (plist
)), Qnil
));
1882 result
= Fcons (Fcons (make_number (s
),
1883 Fcons (make_number (s
+ len
),
1884 Fcons (plist
, Qnil
))),
1887 i
= next_interval (i
);
1898 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1899 (START END PLIST), where START and END are positions and PLIST is a
1900 property list containing the text properties to add. Adjust START
1901 and END positions by DELTA before adding properties. Value is
1902 non-zero if OBJECT was modified. */
1905 add_text_properties_from_list (Lisp_Object object
, Lisp_Object list
, Lisp_Object delta
)
1907 struct gcpro gcpro1
, gcpro2
;
1910 GCPRO2 (list
, object
);
1912 for (; CONSP (list
); list
= XCDR (list
))
1914 Lisp_Object item
, start
, end
, plist
, tem
;
1917 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1918 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1919 plist
= XCAR (XCDR (XCDR (item
)));
1921 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1932 /* Modify end-points of ranges in LIST destructively, and return the
1933 new list. LIST is a list as returned from text_property_list.
1934 Discard properties that begin at or after NEW_END, and limit
1935 end-points to NEW_END. */
1938 extend_property_ranges (Lisp_Object list
, Lisp_Object new_end
)
1940 Lisp_Object prev
= Qnil
, head
= list
;
1941 ptrdiff_t max
= XINT (new_end
);
1943 for (; CONSP (list
); prev
= list
, list
= XCDR (list
))
1945 Lisp_Object item
, beg
, end
;
1949 end
= XCAR (XCDR (item
));
1951 if (XINT (beg
) >= max
)
1953 /* The start-point is past the end of the new string.
1954 Discard this property. */
1955 if (EQ (head
, list
))
1958 XSETCDR (prev
, XCDR (list
));
1960 else if (XINT (end
) > max
)
1961 /* The end-point is past the end of the new string. */
1962 XSETCAR (XCDR (item
), new_end
);
1970 /* Call the modification hook functions in LIST, each with START and END. */
1973 call_mod_hooks (Lisp_Object list
, Lisp_Object start
, Lisp_Object end
)
1975 struct gcpro gcpro1
;
1977 while (!NILP (list
))
1979 call2 (Fcar (list
), start
, end
);
1985 /* Check for read-only intervals between character positions START ... END,
1986 in BUF, and signal an error if we find one.
1988 Then check for any modification hooks in the range.
1989 Create a list of all these hooks in lexicographic order,
1990 eliminating consecutive extra copies of the same hook. Then call
1991 those hooks in order, with START and END - 1 as arguments. */
1994 verify_interval_modification (struct buffer
*buf
,
1995 ptrdiff_t start
, ptrdiff_t end
)
1997 INTERVAL intervals
= buffer_intervals (buf
);
2000 Lisp_Object prev_mod_hooks
;
2001 Lisp_Object mod_hooks
;
2002 struct gcpro gcpro1
;
2005 prev_mod_hooks
= Qnil
;
2008 interval_insert_behind_hooks
= Qnil
;
2009 interval_insert_in_front_hooks
= Qnil
;
2016 ptrdiff_t temp
= start
;
2021 /* For an insert operation, check the two chars around the position. */
2024 INTERVAL prev
= NULL
;
2025 Lisp_Object before
, after
;
2027 /* Set I to the interval containing the char after START,
2028 and PREV to the interval containing the char before START.
2029 Either one may be null. They may be equal. */
2030 i
= find_interval (intervals
, start
);
2032 if (start
== BUF_BEGV (buf
))
2034 else if (i
->position
== start
)
2035 prev
= previous_interval (i
);
2036 else if (i
->position
< start
)
2038 if (start
== BUF_ZV (buf
))
2041 /* If Vinhibit_read_only is set and is not a list, we can
2042 skip the read_only checks. */
2043 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2045 /* If I and PREV differ we need to check for the read-only
2046 property together with its stickiness. If either I or
2047 PREV are 0, this check is all we need.
2048 We have to take special care, since read-only may be
2049 indirectly defined via the category property. */
2054 after
= textget (i
->plist
, Qread_only
);
2056 /* If interval I is read-only and read-only is
2057 front-sticky, inhibit insertion.
2058 Check for read-only as well as category. */
2060 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2064 tem
= textget (i
->plist
, Qfront_sticky
);
2065 if (TMEM (Qread_only
, tem
)
2066 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2067 && TMEM (Qcategory
, tem
)))
2068 text_read_only (after
);
2074 before
= textget (prev
->plist
, Qread_only
);
2076 /* If interval PREV is read-only and read-only isn't
2077 rear-nonsticky, inhibit insertion.
2078 Check for read-only as well as category. */
2080 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2084 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2085 if (! TMEM (Qread_only
, tem
)
2086 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2087 || ! TMEM (Qcategory
, tem
)))
2088 text_read_only (before
);
2094 after
= textget (i
->plist
, Qread_only
);
2096 /* If interval I is read-only and read-only is
2097 front-sticky, inhibit insertion.
2098 Check for read-only as well as category. */
2099 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2103 tem
= textget (i
->plist
, Qfront_sticky
);
2104 if (TMEM (Qread_only
, tem
)
2105 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2106 && TMEM (Qcategory
, tem
)))
2107 text_read_only (after
);
2109 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2110 if (! TMEM (Qread_only
, tem
)
2111 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2112 || ! TMEM (Qcategory
, tem
)))
2113 text_read_only (after
);
2118 /* Run both insert hooks (just once if they're the same). */
2120 interval_insert_behind_hooks
2121 = textget (prev
->plist
, Qinsert_behind_hooks
);
2123 interval_insert_in_front_hooks
2124 = textget (i
->plist
, Qinsert_in_front_hooks
);
2128 /* Loop over intervals on or next to START...END,
2129 collecting their hooks. */
2131 i
= find_interval (intervals
, start
);
2134 if (! INTERVAL_WRITABLE_P (i
))
2135 text_read_only (textget (i
->plist
, Qread_only
));
2137 if (!inhibit_modification_hooks
)
2139 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2140 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2142 hooks
= Fcons (mod_hooks
, hooks
);
2143 prev_mod_hooks
= mod_hooks
;
2147 i
= next_interval (i
);
2149 /* Keep going thru the interval containing the char before END. */
2150 while (i
&& i
->position
< end
);
2152 if (!inhibit_modification_hooks
)
2155 hooks
= Fnreverse (hooks
);
2156 while (! EQ (hooks
, Qnil
))
2158 call_mod_hooks (Fcar (hooks
), make_number (start
),
2160 hooks
= Fcdr (hooks
);
2167 /* Run the interval hooks for an insertion on character range START ... END.
2168 verify_interval_modification chose which hooks to run;
2169 this function is called after the insertion happens
2170 so it can indicate the range of inserted text. */
2173 report_interval_modification (Lisp_Object start
, Lisp_Object end
)
2175 if (! NILP (interval_insert_behind_hooks
))
2176 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2177 if (! NILP (interval_insert_in_front_hooks
)
2178 && ! EQ (interval_insert_in_front_hooks
,
2179 interval_insert_behind_hooks
))
2180 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2184 syms_of_textprop (void)
2186 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties
,
2187 doc
: /* Property-list used as default values.
2188 The value of a property in this list is seen as the value for every
2189 character that does not have its own value for that property. */);
2190 Vdefault_text_properties
= Qnil
;
2192 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist
,
2193 doc
: /* Alist of alternative properties for properties without a value.
2194 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2195 If a piece of text has no direct value for a particular property, then
2196 this alist is consulted. If that property appears in the alist, then
2197 the first non-nil value from the associated alternative properties is
2199 Vchar_property_alias_alist
= Qnil
;
2201 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks
,
2202 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2203 This also inhibits the use of the `intangible' text property. */);
2204 Vinhibit_point_motion_hooks
= Qnil
;
2206 DEFVAR_LISP ("text-property-default-nonsticky",
2207 Vtext_property_default_nonsticky
,
2208 doc
: /* Alist of properties vs the corresponding non-stickiness.
2209 Each element has the form (PROPERTY . NONSTICKINESS).
2211 If a character in a buffer has PROPERTY, new text inserted adjacent to
2212 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2213 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2214 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2215 /* Text properties `syntax-table'and `display' should be nonsticky
2217 Vtext_property_default_nonsticky
2218 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt
),
2219 Fcons (Fcons (intern_c_string ("display"), Qt
), Qnil
));
2221 staticpro (&interval_insert_behind_hooks
);
2222 staticpro (&interval_insert_in_front_hooks
);
2223 interval_insert_behind_hooks
= Qnil
;
2224 interval_insert_in_front_hooks
= Qnil
;
2227 /* Common attributes one might give text */
2229 DEFSYM (Qforeground
, "foreground");
2230 DEFSYM (Qbackground
, "background");
2231 DEFSYM (Qfont
, "font");
2232 DEFSYM (Qstipple
, "stipple");
2233 DEFSYM (Qunderline
, "underline");
2234 DEFSYM (Qread_only
, "read-only");
2235 DEFSYM (Qinvisible
, "invisible");
2236 DEFSYM (Qintangible
, "intangible");
2237 DEFSYM (Qcategory
, "category");
2238 DEFSYM (Qlocal_map
, "local-map");
2239 DEFSYM (Qfront_sticky
, "front-sticky");
2240 DEFSYM (Qrear_nonsticky
, "rear-nonsticky");
2241 DEFSYM (Qmouse_face
, "mouse-face");
2242 DEFSYM (Qminibuffer_prompt
, "minibuffer-prompt");
2244 /* Properties that text might use to specify certain actions */
2246 DEFSYM (Qmouse_left
, "mouse-left");
2247 DEFSYM (Qmouse_entered
, "mouse-entered");
2248 DEFSYM (Qpoint_left
, "point-left");
2249 DEFSYM (Qpoint_entered
, "point-entered");
2251 defsubr (&Stext_properties_at
);
2252 defsubr (&Sget_text_property
);
2253 defsubr (&Sget_char_property
);
2254 defsubr (&Sget_char_property_and_overlay
);
2255 defsubr (&Snext_char_property_change
);
2256 defsubr (&Sprevious_char_property_change
);
2257 defsubr (&Snext_single_char_property_change
);
2258 defsubr (&Sprevious_single_char_property_change
);
2259 defsubr (&Snext_property_change
);
2260 defsubr (&Snext_single_property_change
);
2261 defsubr (&Sprevious_property_change
);
2262 defsubr (&Sprevious_single_property_change
);
2263 defsubr (&Sadd_text_properties
);
2264 defsubr (&Sput_text_property
);
2265 defsubr (&Sset_text_properties
);
2266 defsubr (&Sremove_text_properties
);
2267 defsubr (&Sremove_list_of_text_properties
);
2268 defsubr (&Stext_property_any
);
2269 defsubr (&Stext_property_not_all
);