1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2013 Free Software Foundation,
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
23 #include "intervals.h"
24 #include "character.h"
28 /* Test for membership, allowing for t (actually any non-cons) to mean the
31 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
34 /* NOTES: previous- and next- property change will have to skip
35 zero-length intervals if they are implemented. This could be done
36 inside next_interval and previous_interval.
38 set_properties needs to deal with the interval property cache.
40 It is assumed that for any interval plist, a property appears
41 only once on the list. Although some code i.e., remove_properties,
42 handles the more general case, the uniqueness of properties is
43 necessary for the system to remain consistent. This requirement
44 is enforced by the subrs installing properties onto the intervals. */
48 static Lisp_Object Qmouse_left
;
49 static Lisp_Object Qmouse_entered
;
50 Lisp_Object Qpoint_left
;
51 Lisp_Object Qpoint_entered
;
52 Lisp_Object Qcategory
;
53 Lisp_Object Qlocal_map
;
55 /* Visual properties text (including strings) may have. */
56 static Lisp_Object Qforeground
, Qbackground
, Qunderline
;
58 static Lisp_Object Qstipple
;
59 Lisp_Object Qinvisible
, Qintangible
, Qmouse_face
;
60 static Lisp_Object Qread_only
;
61 Lisp_Object Qminibuffer_prompt
;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
71 /* verify_interval_modification saves insertion hooks here
72 to be run later by report_interval_modification. */
73 static Lisp_Object interval_insert_behind_hooks
;
74 static Lisp_Object interval_insert_in_front_hooks
;
77 /* Signal a `text-read-only' error. This function makes it easier
78 to capture that error in GDB by putting a breakpoint on it. */
81 text_read_only (Lisp_Object propval
)
83 if (STRINGP (propval
))
84 xsignal1 (Qtext_read_only
, propval
);
86 xsignal0 (Qtext_read_only
);
91 /* Extract the interval at the position pointed to by BEGIN from
92 OBJECT, a string or buffer. Additionally, check that the positions
93 pointed to by BEGIN and END are within the bounds of OBJECT, and
94 reverse them if *BEGIN is greater than *END. The objects pointed
95 to by BEGIN and END may be integers or markers; if the latter, they
96 are coerced to integers.
98 When OBJECT is a string, we increment *BEGIN and *END
99 to make them origin-one.
101 Note that buffer points don't correspond to interval indices.
102 For example, point-max is 1 greater than the index of the last
103 character. This difference is handled in the caller, which uses
104 the validated points to determine a length, and operates on that.
105 Exceptions are Ftext_properties_at, Fnext_property_change, and
106 Fprevious_property_change which call this function with BEGIN == END.
107 Handle this case specially.
109 If FORCE is soft (0), it's OK to return NULL. Otherwise,
110 create an interval tree for OBJECT if one doesn't exist, provided
111 the object actually contains text. In the current design, if there
112 is no text, there can be no text properties. */
118 validate_interval_range (Lisp_Object object
, Lisp_Object
*begin
, Lisp_Object
*end
, int force
)
123 CHECK_STRING_OR_BUFFER (object
);
124 CHECK_NUMBER_COERCE_MARKER (*begin
);
125 CHECK_NUMBER_COERCE_MARKER (*end
);
127 /* If we are asked for a point, but from a subr which operates
128 on a range, then return nothing. */
129 if (EQ (*begin
, *end
) && begin
!= end
)
132 if (XINT (*begin
) > XINT (*end
))
140 if (BUFFERP (object
))
142 register struct buffer
*b
= XBUFFER (object
);
144 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
145 && XINT (*end
) <= BUF_ZV (b
)))
146 args_out_of_range (*begin
, *end
);
147 i
= buffer_intervals (b
);
149 /* If there's no text, there are no properties. */
150 if (BUF_BEGV (b
) == BUF_ZV (b
))
153 searchpos
= XINT (*begin
);
157 ptrdiff_t len
= SCHARS (object
);
159 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
160 && XINT (*end
) <= len
))
161 args_out_of_range (*begin
, *end
);
162 XSETFASTINT (*begin
, XFASTINT (*begin
));
164 XSETFASTINT (*end
, XFASTINT (*end
));
165 i
= string_intervals (object
);
170 searchpos
= XINT (*begin
);
174 return (force
? create_root_interval (object
) : i
);
176 return find_interval (i
, searchpos
);
179 /* Validate LIST as a property list. If LIST is not a list, then
180 make one consisting of (LIST nil). Otherwise, verify that LIST
181 is even numbered and thus suitable as a plist. */
184 validate_plist (Lisp_Object list
)
192 register Lisp_Object tail
;
193 for (i
= 0, tail
= list
; CONSP (tail
); i
++)
199 error ("Odd length text property list");
203 return Fcons (list
, Fcons (Qnil
, Qnil
));
206 /* Return nonzero if interval I has all the properties,
207 with the same values, of list PLIST. */
210 interval_has_all_properties (Lisp_Object plist
, INTERVAL i
)
212 register Lisp_Object tail1
, tail2
, sym1
;
215 /* Go through each element of PLIST. */
216 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
221 /* Go through I's plist, looking for sym1 */
222 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
223 if (EQ (sym1
, XCAR (tail2
)))
225 /* Found the same property on both lists. If the
226 values are unequal, return zero. */
227 if (! EQ (Fcar (XCDR (tail1
)), Fcar (XCDR (tail2
))))
230 /* Property has same value on both lists; go to next one. */
242 /* Return nonzero if the plist of interval I has any of the
243 properties of PLIST, regardless of their values. */
246 interval_has_some_properties (Lisp_Object plist
, INTERVAL i
)
248 register Lisp_Object tail1
, tail2
, sym
;
250 /* Go through each element of PLIST. */
251 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
255 /* Go through i's plist, looking for tail1 */
256 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
257 if (EQ (sym
, XCAR (tail2
)))
264 /* Return nonzero if the plist of interval I has any of the
265 property names in LIST, regardless of their values. */
268 interval_has_some_properties_list (Lisp_Object list
, INTERVAL i
)
270 register Lisp_Object tail1
, tail2
, sym
;
272 /* Go through each element of LIST. */
273 for (tail1
= list
; CONSP (tail1
); tail1
= XCDR (tail1
))
277 /* Go through i's plist, looking for tail1 */
278 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= XCDR (XCDR (tail2
)))
279 if (EQ (sym
, XCAR (tail2
)))
286 /* Changing the plists of individual intervals. */
288 /* Return the value of PROP in property-list PLIST, or Qunbound if it
291 property_value (Lisp_Object plist
, Lisp_Object prop
)
295 while (PLIST_ELT_P (plist
, value
))
296 if (EQ (XCAR (plist
), prop
))
299 plist
= XCDR (value
);
304 /* Set the properties of INTERVAL to PROPERTIES,
305 and record undo info for the previous values.
306 OBJECT is the string or buffer that INTERVAL belongs to. */
309 set_properties (Lisp_Object properties
, INTERVAL interval
, Lisp_Object object
)
311 Lisp_Object sym
, value
;
313 if (BUFFERP (object
))
315 /* For each property in the old plist which is missing from PROPERTIES,
316 or has a different value in PROPERTIES, make an undo record. */
317 for (sym
= interval
->plist
;
318 PLIST_ELT_P (sym
, value
);
320 if (! EQ (property_value (properties
, XCAR (sym
)),
323 record_property_change (interval
->position
, LENGTH (interval
),
324 XCAR (sym
), XCAR (value
),
328 /* For each new property that has no value at all in the old plist,
329 make an undo record binding it to nil, so it will be removed. */
330 for (sym
= properties
;
331 PLIST_ELT_P (sym
, value
);
333 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
335 record_property_change (interval
->position
, LENGTH (interval
),
341 /* Store new properties. */
342 set_interval_plist (interval
, Fcopy_sequence (properties
));
345 /* Add the properties of PLIST to the interval I, or set
346 the value of I's property to the value of the property on PLIST
347 if they are different.
349 OBJECT should be the string or buffer the interval is in.
351 Return nonzero if this changes I (i.e., if any members of PLIST
352 are actually added to I's plist) */
355 add_properties (Lisp_Object plist
, INTERVAL i
, Lisp_Object object
)
357 Lisp_Object tail1
, tail2
, sym1
, val1
;
358 register int changed
= 0;
360 struct gcpro gcpro1
, gcpro2
, gcpro3
;
365 /* No need to protect OBJECT, because we can GC only in the case
366 where it is a buffer, and live buffers are always protected.
367 I and its plist are also protected, via OBJECT. */
368 GCPRO3 (tail1
, sym1
, val1
);
370 /* Go through each element of PLIST. */
371 for (tail1
= plist
; CONSP (tail1
); tail1
= Fcdr (XCDR (tail1
)))
374 val1
= Fcar (XCDR (tail1
));
377 /* Go through I's plist, looking for sym1 */
378 for (tail2
= i
->plist
; CONSP (tail2
); tail2
= Fcdr (XCDR (tail2
)))
379 if (EQ (sym1
, XCAR (tail2
)))
381 /* No need to gcpro, because tail2 protects this
382 and it must be a cons cell (we get an error otherwise). */
383 register Lisp_Object this_cdr
;
385 this_cdr
= XCDR (tail2
);
386 /* Found the property. Now check its value. */
389 /* The properties have the same value on both lists.
390 Continue to the next property. */
391 if (EQ (val1
, Fcar (this_cdr
)))
394 /* Record this change in the buffer, for undo purposes. */
395 if (BUFFERP (object
))
397 record_property_change (i
->position
, LENGTH (i
),
398 sym1
, Fcar (this_cdr
), object
);
401 /* I's property has a different value -- change it */
402 Fsetcar (this_cdr
, val1
);
409 /* Record this change in the buffer, for undo purposes. */
410 if (BUFFERP (object
))
412 record_property_change (i
->position
, LENGTH (i
),
415 set_interval_plist (i
, Fcons (sym1
, Fcons (val1
, i
->plist
)));
425 /* For any members of PLIST, or LIST,
426 which are properties of I, remove them from I's plist.
427 (If PLIST is non-nil, use that, otherwise use LIST.)
428 OBJECT is the string or buffer containing I. */
431 remove_properties (Lisp_Object plist
, Lisp_Object list
, INTERVAL i
, Lisp_Object object
)
433 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
434 register int changed
= 0;
436 /* Nonzero means tail1 is a plist, otherwise it is a list. */
439 current_plist
= i
->plist
;
442 tail1
= plist
, use_plist
= 1;
444 tail1
= list
, use_plist
= 0;
446 /* Go through each element of LIST or PLIST. */
447 while (CONSP (tail1
))
451 /* First, remove the symbol if it's at the head of the list */
452 while (CONSP (current_plist
) && EQ (sym
, XCAR (current_plist
)))
454 if (BUFFERP (object
))
455 record_property_change (i
->position
, LENGTH (i
),
456 sym
, XCAR (XCDR (current_plist
)),
459 current_plist
= XCDR (XCDR (current_plist
));
463 /* Go through I's plist, looking for SYM. */
464 tail2
= current_plist
;
465 while (! NILP (tail2
))
467 register Lisp_Object
this;
468 this = XCDR (XCDR (tail2
));
469 if (CONSP (this) && EQ (sym
, XCAR (this)))
471 if (BUFFERP (object
))
472 record_property_change (i
->position
, LENGTH (i
),
473 sym
, XCAR (XCDR (this)), object
);
475 Fsetcdr (XCDR (tail2
), XCDR (XCDR (this)));
481 /* Advance thru TAIL1 one way or the other. */
482 tail1
= XCDR (tail1
);
483 if (use_plist
&& CONSP (tail1
))
484 tail1
= XCDR (tail1
);
488 set_interval_plist (i
, current_plist
);
492 /* Returns the interval of POSITION in OBJECT.
493 POSITION is BEG-based. */
496 interval_of (ptrdiff_t position
, Lisp_Object object
)
502 XSETBUFFER (object
, current_buffer
);
503 else if (EQ (object
, Qt
))
506 CHECK_STRING_OR_BUFFER (object
);
508 if (BUFFERP (object
))
510 register struct buffer
*b
= XBUFFER (object
);
514 i
= buffer_intervals (b
);
519 end
= SCHARS (object
);
520 i
= string_intervals (object
);
523 if (!(beg
<= position
&& position
<= end
))
524 args_out_of_range (make_number (position
), make_number (position
));
525 if (beg
== end
|| !i
)
528 return find_interval (i
, position
);
531 DEFUN ("text-properties-at", Ftext_properties_at
,
532 Stext_properties_at
, 1, 2, 0,
533 doc
: /* Return the list of properties of the character at POSITION in OBJECT.
534 If the optional second argument OBJECT is a buffer (or nil, which means
535 the current buffer), POSITION is a buffer position (integer or marker).
536 If OBJECT is a string, POSITION is a 0-based index into it.
537 If POSITION is at the end of OBJECT, the value is nil. */)
538 (Lisp_Object position
, Lisp_Object object
)
543 XSETBUFFER (object
, current_buffer
);
545 i
= validate_interval_range (object
, &position
, &position
, soft
);
548 /* If POSITION is at the end of the interval,
549 it means it's the end of OBJECT.
550 There are no properties at the very end,
551 since no character follows. */
552 if (XINT (position
) == LENGTH (i
) + i
->position
)
558 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
559 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
560 OBJECT should be a buffer or a string; if omitted or nil, it defaults
561 to the current buffer.
562 If POSITION is at the end of OBJECT, the value is nil. */)
563 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
565 return textget (Ftext_properties_at (position
, object
), prop
);
568 /* Return the value of char's property PROP, in OBJECT at POSITION.
569 OBJECT is optional and defaults to the current buffer.
570 If OVERLAY is non-0, then in the case that the returned property is from
571 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
572 returned in *OVERLAY.
573 If POSITION is at the end of OBJECT, the value is nil.
574 If OBJECT is a buffer, then overlay properties are considered as well as
576 If OBJECT is a window, then that window's buffer is used, but
577 window-specific overlays are considered only if they are associated
580 get_char_property_and_overlay (Lisp_Object position
, register Lisp_Object prop
, Lisp_Object object
, Lisp_Object
*overlay
)
582 struct window
*w
= 0;
584 CHECK_NUMBER_COERCE_MARKER (position
);
587 XSETBUFFER (object
, current_buffer
);
589 if (WINDOWP (object
))
591 w
= XWINDOW (object
);
594 if (BUFFERP (object
))
597 Lisp_Object
*overlay_vec
;
598 struct buffer
*obuf
= current_buffer
;
600 if (XINT (position
) < BUF_BEGV (XBUFFER (object
))
601 || XINT (position
) > BUF_ZV (XBUFFER (object
)))
602 xsignal1 (Qargs_out_of_range
, position
);
604 set_buffer_temp (XBUFFER (object
));
606 GET_OVERLAYS_AT (XINT (position
), overlay_vec
, noverlays
, NULL
, 0);
607 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
609 set_buffer_temp (obuf
);
611 /* Now check the overlays in order of decreasing priority. */
612 while (--noverlays
>= 0)
614 Lisp_Object tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
618 /* Return the overlay we got the property from. */
619 *overlay
= overlay_vec
[noverlays
];
626 /* Indicate that the return value is not from an overlay. */
629 /* Not a buffer, or no appropriate overlay, so fall through to the
631 return Fget_text_property (position
, prop
, object
);
634 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
635 doc
: /* Return the value of POSITION's property PROP, in OBJECT.
636 Both overlay properties and text properties are checked.
637 OBJECT is optional and defaults to the current buffer.
638 If POSITION is at the end of OBJECT, the value is nil.
639 If OBJECT is a buffer, then overlay properties are considered as well as
641 If OBJECT is a window, then that window's buffer is used, but window-specific
642 overlays are considered only if they are associated with OBJECT. */)
643 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
645 return get_char_property_and_overlay (position
, prop
, object
, 0);
648 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay
,
649 Sget_char_property_and_overlay
, 2, 3, 0,
650 doc
: /* Like `get-char-property', but with extra overlay information.
651 The value is a cons cell. Its car is the return value of `get-char-property'
652 with the same arguments--that is, the value of POSITION's property
653 PROP in OBJECT. Its cdr is the overlay in which the property was
654 found, or nil, if it was found as a text property or not found at all.
656 OBJECT is optional and defaults to the current buffer. OBJECT may be
657 a string, a buffer or a window. For strings, the cdr of the return
658 value is always nil, since strings do not have overlays. If OBJECT is
659 a window, then that window's buffer is used, but window-specific
660 overlays are considered only if they are associated with OBJECT. If
661 POSITION is at the end of OBJECT, both car and cdr are nil. */)
662 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
)
666 = get_char_property_and_overlay (position
, prop
, object
, &overlay
);
667 return Fcons (val
, overlay
);
671 DEFUN ("next-char-property-change", Fnext_char_property_change
,
672 Snext_char_property_change
, 1, 2, 0,
673 doc
: /* Return the position of next text property or overlay change.
674 This scans characters forward in the current buffer from POSITION till
675 it finds a change in some text property, or the beginning or end of an
676 overlay, and returns the position of that.
677 If none is found up to (point-max), the function returns (point-max).
679 If the optional second argument LIMIT is non-nil, don't search
680 past position LIMIT; return LIMIT if nothing is found before LIMIT.
681 LIMIT is a no-op if it is greater than (point-max). */)
682 (Lisp_Object position
, Lisp_Object limit
)
686 temp
= Fnext_overlay_change (position
);
689 CHECK_NUMBER_COERCE_MARKER (limit
);
690 if (XINT (limit
) < XINT (temp
))
693 return Fnext_property_change (position
, Qnil
, temp
);
696 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
697 Sprevious_char_property_change
, 1, 2, 0,
698 doc
: /* Return the position of previous text property or overlay change.
699 Scans characters backward in the current buffer from POSITION till it
700 finds a change in some text property, or the beginning or end of an
701 overlay, and returns the position of that.
702 If none is found since (point-min), the function returns (point-min).
704 If the optional second argument LIMIT is non-nil, don't search
705 past position LIMIT; return LIMIT if nothing is found before LIMIT.
706 LIMIT is a no-op if it is less than (point-min). */)
707 (Lisp_Object position
, Lisp_Object limit
)
711 temp
= Fprevious_overlay_change (position
);
714 CHECK_NUMBER_COERCE_MARKER (limit
);
715 if (XINT (limit
) > XINT (temp
))
718 return Fprevious_property_change (position
, Qnil
, temp
);
722 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
723 Snext_single_char_property_change
, 2, 4, 0,
724 doc
: /* Return the position of next text property or overlay change for a specific property.
725 Scans characters forward from POSITION till it finds
726 a change in the PROP property, then returns the position of the change.
727 If the optional third argument OBJECT is a buffer (or nil, which means
728 the current buffer), POSITION is a buffer position (integer or marker).
729 If OBJECT is a string, POSITION is a 0-based index into it.
731 In a string, scan runs to the end of the string.
732 In a buffer, it runs to (point-max), and the value cannot exceed that.
734 The property values are compared with `eq'.
735 If the property is constant all the way to the end of OBJECT, return the
736 last valid position in OBJECT.
737 If the optional fourth argument LIMIT is non-nil, don't search
738 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
739 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
741 if (STRINGP (object
))
743 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
747 position
= make_number (SCHARS (object
));
750 CHECK_NUMBER (limit
);
757 Lisp_Object initial_value
, value
;
758 ptrdiff_t count
= SPECPDL_INDEX ();
761 CHECK_BUFFER (object
);
763 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
765 record_unwind_current_buffer ();
766 Fset_buffer (object
);
769 CHECK_NUMBER_COERCE_MARKER (position
);
771 initial_value
= Fget_char_property (position
, prop
, object
);
774 XSETFASTINT (limit
, ZV
);
776 CHECK_NUMBER_COERCE_MARKER (limit
);
778 if (XFASTINT (position
) >= XFASTINT (limit
))
781 if (XFASTINT (position
) > ZV
)
782 XSETFASTINT (position
, ZV
);
787 position
= Fnext_char_property_change (position
, limit
);
788 if (XFASTINT (position
) >= XFASTINT (limit
))
794 value
= Fget_char_property (position
, prop
, object
);
795 if (!EQ (value
, initial_value
))
799 unbind_to (count
, Qnil
);
805 DEFUN ("previous-single-char-property-change",
806 Fprevious_single_char_property_change
,
807 Sprevious_single_char_property_change
, 2, 4, 0,
808 doc
: /* Return the position of previous text property or overlay change for a specific property.
809 Scans characters backward from POSITION till it finds
810 a change in the PROP property, then returns the position of the change.
811 If the optional third argument OBJECT is a buffer (or nil, which means
812 the current buffer), POSITION is a buffer position (integer or marker).
813 If OBJECT is a string, POSITION is a 0-based index into it.
815 In a string, scan runs to the start of the string.
816 In a buffer, it runs to (point-min), and the value cannot be less than that.
818 The property values are compared with `eq'.
819 If the property is constant all the way to the start of OBJECT, return the
820 first valid position in OBJECT.
821 If the optional fourth argument LIMIT is non-nil, don't search back past
822 position LIMIT; return LIMIT if nothing is found before reaching LIMIT. */)
823 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
825 if (STRINGP (object
))
827 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
831 position
= make_number (0);
834 CHECK_NUMBER (limit
);
841 ptrdiff_t count
= SPECPDL_INDEX ();
844 CHECK_BUFFER (object
);
846 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
848 record_unwind_current_buffer ();
849 Fset_buffer (object
);
852 CHECK_NUMBER_COERCE_MARKER (position
);
855 XSETFASTINT (limit
, BEGV
);
857 CHECK_NUMBER_COERCE_MARKER (limit
);
859 if (XFASTINT (position
) <= XFASTINT (limit
))
862 if (XFASTINT (position
) < BEGV
)
863 XSETFASTINT (position
, BEGV
);
867 Lisp_Object initial_value
868 = Fget_char_property (make_number (XFASTINT (position
) - 1),
873 position
= Fprevious_char_property_change (position
, limit
);
875 if (XFASTINT (position
) <= XFASTINT (limit
))
883 = Fget_char_property (make_number (XFASTINT (position
) - 1),
886 if (!EQ (value
, initial_value
))
892 unbind_to (count
, Qnil
);
898 DEFUN ("next-property-change", Fnext_property_change
,
899 Snext_property_change
, 1, 3, 0,
900 doc
: /* Return the position of next property change.
901 Scans characters forward from POSITION in OBJECT till it finds
902 a change in some text property, then returns the position of the change.
903 If the optional second argument OBJECT is a buffer (or nil, which means
904 the current buffer), POSITION is a buffer position (integer or marker).
905 If OBJECT is a string, POSITION is a 0-based index into it.
906 Return nil if the property is constant all the way to the end of OBJECT.
907 If the value is non-nil, it is a position greater than POSITION, never equal.
909 If the optional third argument LIMIT is non-nil, don't search
910 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
911 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
913 register INTERVAL i
, next
;
916 XSETBUFFER (object
, current_buffer
);
918 if (!NILP (limit
) && !EQ (limit
, Qt
))
919 CHECK_NUMBER_COERCE_MARKER (limit
);
921 i
= validate_interval_range (object
, &position
, &position
, soft
);
923 /* If LIMIT is t, return start of next interval--don't
924 bother checking further intervals. */
930 next
= next_interval (i
);
933 XSETFASTINT (position
, (STRINGP (object
)
935 : BUF_ZV (XBUFFER (object
))));
937 XSETFASTINT (position
, next
->position
);
944 next
= next_interval (i
);
946 while (next
&& intervals_equal (i
, next
)
947 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
948 next
= next_interval (next
);
956 : BUF_ZV (XBUFFER (object
))))))
959 return make_number (next
->position
);
962 DEFUN ("next-single-property-change", Fnext_single_property_change
,
963 Snext_single_property_change
, 2, 4, 0,
964 doc
: /* Return the position of next property change for a specific property.
965 Scans characters forward from POSITION till it finds
966 a change in the PROP property, then returns the position of the change.
967 If the optional third argument OBJECT is a buffer (or nil, which means
968 the current buffer), POSITION is a buffer position (integer or marker).
969 If OBJECT is a string, POSITION is a 0-based index into it.
970 The property values are compared with `eq'.
971 Return nil if the property is constant all the way to the end of OBJECT.
972 If the value is non-nil, it is a position greater than POSITION, never equal.
974 If the optional fourth argument LIMIT is non-nil, don't search
975 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
976 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
978 register INTERVAL i
, next
;
979 register Lisp_Object here_val
;
982 XSETBUFFER (object
, current_buffer
);
985 CHECK_NUMBER_COERCE_MARKER (limit
);
987 i
= validate_interval_range (object
, &position
, &position
, soft
);
991 here_val
= textget (i
->plist
, prop
);
992 next
= next_interval (i
);
994 && EQ (here_val
, textget (next
->plist
, prop
))
995 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
996 next
= next_interval (next
);
1000 >= (INTEGERP (limit
)
1004 : BUF_ZV (XBUFFER (object
))))))
1007 return make_number (next
->position
);
1010 DEFUN ("previous-property-change", Fprevious_property_change
,
1011 Sprevious_property_change
, 1, 3, 0,
1012 doc
: /* Return the position of previous property change.
1013 Scans characters backwards from POSITION in OBJECT till it finds
1014 a change in some text property, then returns the position of the change.
1015 If the optional second argument OBJECT is a buffer (or nil, which means
1016 the current buffer), POSITION is a buffer position (integer or marker).
1017 If OBJECT is a string, POSITION is a 0-based index into it.
1018 Return nil if the property is constant all the way to the start of OBJECT.
1019 If the value is non-nil, it is a position less than POSITION, never equal.
1021 If the optional third argument LIMIT is non-nil, don't search
1022 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1023 (Lisp_Object position
, Lisp_Object object
, Lisp_Object limit
)
1025 register INTERVAL i
, previous
;
1028 XSETBUFFER (object
, current_buffer
);
1031 CHECK_NUMBER_COERCE_MARKER (limit
);
1033 i
= validate_interval_range (object
, &position
, &position
, soft
);
1037 /* Start with the interval containing the char before point. */
1038 if (i
->position
== XFASTINT (position
))
1039 i
= previous_interval (i
);
1041 previous
= previous_interval (i
);
1042 while (previous
&& intervals_equal (previous
, i
)
1044 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1045 previous
= previous_interval (previous
);
1048 || (previous
->position
+ LENGTH (previous
)
1049 <= (INTEGERP (limit
)
1051 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1054 return make_number (previous
->position
+ LENGTH (previous
));
1057 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1058 Sprevious_single_property_change
, 2, 4, 0,
1059 doc
: /* Return the position of previous property change for a specific property.
1060 Scans characters backward from POSITION till it finds
1061 a change in the PROP property, then returns the position of the change.
1062 If the optional third argument OBJECT is a buffer (or nil, which means
1063 the current buffer), POSITION is a buffer position (integer or marker).
1064 If OBJECT is a string, POSITION is a 0-based index into it.
1065 The property values are compared with `eq'.
1066 Return nil if the property is constant all the way to the start of OBJECT.
1067 If the value is non-nil, it is a position less than POSITION, never equal.
1069 If the optional fourth argument LIMIT is non-nil, don't search
1070 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1071 (Lisp_Object position
, Lisp_Object prop
, Lisp_Object object
, Lisp_Object limit
)
1073 register INTERVAL i
, previous
;
1074 register Lisp_Object here_val
;
1077 XSETBUFFER (object
, current_buffer
);
1080 CHECK_NUMBER_COERCE_MARKER (limit
);
1082 i
= validate_interval_range (object
, &position
, &position
, soft
);
1084 /* Start with the interval containing the char before point. */
1085 if (i
&& i
->position
== XFASTINT (position
))
1086 i
= previous_interval (i
);
1091 here_val
= textget (i
->plist
, prop
);
1092 previous
= previous_interval (i
);
1094 && EQ (here_val
, textget (previous
->plist
, prop
))
1096 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1097 previous
= previous_interval (previous
);
1100 || (previous
->position
+ LENGTH (previous
)
1101 <= (INTEGERP (limit
)
1103 : (STRINGP (object
) ? 0 : BUF_BEGV (XBUFFER (object
))))))
1106 return make_number (previous
->position
+ LENGTH (previous
));
1109 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1111 DEFUN ("add-text-properties", Fadd_text_properties
,
1112 Sadd_text_properties
, 3, 4, 0,
1113 doc
: /* Add properties to the text from START to END.
1114 The third argument PROPERTIES is a property list
1115 specifying the property values to add. If the optional fourth argument
1116 OBJECT is a buffer (or nil, which means the current buffer),
1117 START and END are buffer positions (integers or markers).
1118 If OBJECT is a string, START and END are 0-based indices into it.
1119 Return t if any property value actually changed, nil otherwise. */)
1120 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1122 register INTERVAL i
, unchanged
;
1123 register ptrdiff_t s
, len
;
1124 register int modified
= 0;
1125 struct gcpro gcpro1
;
1127 properties
= validate_plist (properties
);
1128 if (NILP (properties
))
1132 XSETBUFFER (object
, current_buffer
);
1134 i
= validate_interval_range (object
, &start
, &end
, hard
);
1139 len
= XINT (end
) - s
;
1141 /* No need to protect OBJECT, because we GC only if it's a buffer,
1142 and live buffers are always protected. */
1143 GCPRO1 (properties
);
1145 /* If we're not starting on an interval boundary, we have to
1146 split this interval. */
1147 if (i
->position
!= s
)
1149 /* If this interval already has the properties, we can
1151 if (interval_has_all_properties (properties
, i
))
1153 ptrdiff_t got
= (LENGTH (i
) - (s
- i
->position
));
1155 RETURN_UNGCPRO (Qnil
);
1157 i
= next_interval (i
);
1162 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1163 copy_properties (unchanged
, i
);
1167 if (BUFFERP (object
))
1168 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1170 /* We are at the beginning of interval I, with LEN chars to scan. */
1175 if (LENGTH (i
) >= len
)
1177 /* We can UNGCPRO safely here, because there will be just
1178 one more chance to gc, in the next call to add_properties,
1179 and after that we will not need PROPERTIES or OBJECT again. */
1182 if (interval_has_all_properties (properties
, i
))
1184 if (BUFFERP (object
))
1185 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1186 XINT (end
) - XINT (start
));
1188 return modified
? Qt
: Qnil
;
1191 if (LENGTH (i
) == len
)
1193 add_properties (properties
, i
, object
);
1194 if (BUFFERP (object
))
1195 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1196 XINT (end
) - XINT (start
));
1200 /* i doesn't have the properties, and goes past the change limit */
1202 i
= split_interval_left (unchanged
, len
);
1203 copy_properties (unchanged
, i
);
1204 add_properties (properties
, i
, object
);
1205 if (BUFFERP (object
))
1206 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1207 XINT (end
) - XINT (start
));
1212 modified
+= add_properties (properties
, i
, object
);
1213 i
= next_interval (i
);
1217 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1219 DEFUN ("put-text-property", Fput_text_property
,
1220 Sput_text_property
, 4, 5, 0,
1221 doc
: /* Set one property of the text from START to END.
1222 The third and fourth arguments PROPERTY and VALUE
1223 specify the property to add.
1224 If the optional fifth argument OBJECT is a buffer (or nil, which means
1225 the current buffer), START and END are buffer positions (integers or
1226 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1227 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1229 Fadd_text_properties (start
, end
,
1230 Fcons (property
, Fcons (value
, Qnil
)),
1235 DEFUN ("set-text-properties", Fset_text_properties
,
1236 Sset_text_properties
, 3, 4, 0,
1237 doc
: /* Completely replace properties of text from START to END.
1238 The third argument PROPERTIES is the new property list.
1239 If the optional fourth argument OBJECT is a buffer (or nil, which means
1240 the current buffer), START and END are buffer positions (integers or
1241 markers). If OBJECT is a string, START and END are 0-based indices into it.
1242 If PROPERTIES is nil, the effect is to remove all properties from
1243 the designated part of OBJECT. */)
1244 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1246 return set_text_properties (start
, end
, properties
, object
, Qt
);
1250 /* Replace properties of text from START to END with new list of
1251 properties PROPERTIES. OBJECT is the buffer or string containing
1252 the text. OBJECT nil means use the current buffer.
1253 COHERENT_CHANGE_P nil means this is being called as an internal
1254 subroutine, rather than as a change primitive with checking of
1255 read-only, invoking change hooks, etc.. Value is nil if the
1256 function _detected_ that it did not replace any properties, non-nil
1260 set_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
, Lisp_Object coherent_change_p
)
1262 register INTERVAL i
;
1263 Lisp_Object ostart
, oend
;
1268 properties
= validate_plist (properties
);
1271 XSETBUFFER (object
, current_buffer
);
1273 /* If we want no properties for a whole string,
1274 get rid of its intervals. */
1275 if (NILP (properties
) && STRINGP (object
)
1276 && XFASTINT (start
) == 0
1277 && XFASTINT (end
) == SCHARS (object
))
1279 if (!string_intervals (object
))
1282 set_string_intervals (object
, NULL
);
1286 i
= validate_interval_range (object
, &start
, &end
, soft
);
1290 /* If buffer has no properties, and we want none, return now. */
1291 if (NILP (properties
))
1294 /* Restore the original START and END values
1295 because validate_interval_range increments them for strings. */
1299 i
= validate_interval_range (object
, &start
, &end
, hard
);
1300 /* This can return if start == end. */
1305 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1306 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1308 set_text_properties_1 (start
, end
, properties
, object
, i
);
1310 if (BUFFERP (object
) && !NILP (coherent_change_p
))
1311 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1312 XINT (end
) - XINT (start
));
1316 /* Replace properties of text from START to END with new list of
1317 properties PROPERTIES. BUFFER is the buffer containing
1318 the text. This does not obey any hooks.
1319 You can provide the interval that START is located in as I,
1320 or pass NULL for I and this function will find it.
1321 START and END can be in any order. */
1324 set_text_properties_1 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object buffer
, INTERVAL i
)
1326 register INTERVAL prev_changed
= NULL
;
1327 register ptrdiff_t s
, len
;
1330 if (XINT (start
) < XINT (end
))
1333 len
= XINT (end
) - s
;
1335 else if (XINT (end
) < XINT (start
))
1338 len
= XINT (start
) - s
;
1344 i
= find_interval (buffer_intervals (XBUFFER (buffer
)), s
);
1346 if (i
->position
!= s
)
1349 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1351 if (LENGTH (i
) > len
)
1353 copy_properties (unchanged
, i
);
1354 i
= split_interval_left (i
, len
);
1355 set_properties (properties
, i
, buffer
);
1359 set_properties (properties
, i
, buffer
);
1361 if (LENGTH (i
) == len
)
1366 i
= next_interval (i
);
1369 /* We are starting at the beginning of an interval I. LEN is positive. */
1374 if (LENGTH (i
) >= len
)
1376 if (LENGTH (i
) > len
)
1377 i
= split_interval_left (i
, len
);
1379 /* We have to call set_properties even if we are going to
1380 merge the intervals, so as to make the undo records
1381 and cause redisplay to happen. */
1382 set_properties (properties
, i
, buffer
);
1384 merge_interval_left (i
);
1390 /* We have to call set_properties even if we are going to
1391 merge the intervals, so as to make the undo records
1392 and cause redisplay to happen. */
1393 set_properties (properties
, i
, buffer
);
1397 prev_changed
= i
= merge_interval_left (i
);
1399 i
= next_interval (i
);
1404 DEFUN ("remove-text-properties", Fremove_text_properties
,
1405 Sremove_text_properties
, 3, 4, 0,
1406 doc
: /* Remove some properties from text from START to END.
1407 The third argument PROPERTIES is a property list
1408 whose property names specify the properties to remove.
1409 \(The values stored in PROPERTIES are ignored.)
1410 If the optional fourth argument OBJECT is a buffer (or nil, which means
1411 the current buffer), START and END are buffer positions (integers or
1412 markers). If OBJECT is a string, START and END are 0-based indices into it.
1413 Return t if any property was actually removed, nil otherwise.
1415 Use `set-text-properties' if you want to remove all text properties. */)
1416 (Lisp_Object start
, Lisp_Object end
, Lisp_Object properties
, Lisp_Object object
)
1418 register INTERVAL i
, unchanged
;
1419 register ptrdiff_t s
, len
;
1420 register int modified
= 0;
1423 XSETBUFFER (object
, current_buffer
);
1425 i
= validate_interval_range (object
, &start
, &end
, soft
);
1430 len
= XINT (end
) - s
;
1432 if (i
->position
!= s
)
1434 /* No properties on this first interval -- return if
1435 it covers the entire region. */
1436 if (! interval_has_some_properties (properties
, i
))
1438 ptrdiff_t got
= (LENGTH (i
) - (s
- i
->position
));
1442 i
= next_interval (i
);
1444 /* Split away the beginning of this interval; what we don't
1449 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1450 copy_properties (unchanged
, i
);
1454 if (BUFFERP (object
))
1455 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1457 /* We are at the beginning of an interval, with len to scan */
1462 if (LENGTH (i
) >= len
)
1464 if (! interval_has_some_properties (properties
, i
))
1465 return modified
? Qt
: Qnil
;
1467 if (LENGTH (i
) == len
)
1469 remove_properties (properties
, Qnil
, i
, object
);
1470 if (BUFFERP (object
))
1471 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1472 XINT (end
) - XINT (start
));
1476 /* i has the properties, and goes past the change limit */
1478 i
= split_interval_left (i
, len
);
1479 copy_properties (unchanged
, i
);
1480 remove_properties (properties
, Qnil
, i
, object
);
1481 if (BUFFERP (object
))
1482 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1483 XINT (end
) - XINT (start
));
1488 modified
+= remove_properties (properties
, Qnil
, i
, object
);
1489 i
= next_interval (i
);
1493 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties
,
1494 Sremove_list_of_text_properties
, 3, 4, 0,
1495 doc
: /* Remove some properties from text from START to END.
1496 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1497 If the optional fourth argument OBJECT is a buffer (or nil, which means
1498 the current buffer), START and END are buffer positions (integers or
1499 markers). If OBJECT is a string, START and END are 0-based indices into it.
1500 Return t if any property was actually removed, nil otherwise. */)
1501 (Lisp_Object start
, Lisp_Object end
, Lisp_Object list_of_properties
, Lisp_Object object
)
1503 register INTERVAL i
, unchanged
;
1504 register ptrdiff_t s
, len
;
1505 register int modified
= 0;
1506 Lisp_Object properties
;
1507 properties
= list_of_properties
;
1510 XSETBUFFER (object
, current_buffer
);
1512 i
= validate_interval_range (object
, &start
, &end
, soft
);
1517 len
= XINT (end
) - s
;
1519 if (i
->position
!= s
)
1521 /* No properties on this first interval -- return if
1522 it covers the entire region. */
1523 if (! interval_has_some_properties_list (properties
, i
))
1525 ptrdiff_t got
= (LENGTH (i
) - (s
- i
->position
));
1529 i
= next_interval (i
);
1531 /* Split away the beginning of this interval; what we don't
1536 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1537 copy_properties (unchanged
, i
);
1541 /* We are at the beginning of an interval, with len to scan.
1542 The flag `modified' records if changes have been made.
1543 When object is a buffer, we must call modify_region before changes are
1544 made and signal_after_change when we are done.
1545 We call modify_region before calling remove_properties if modified == 0,
1546 and we call signal_after_change before returning if modified != 0. */
1551 if (LENGTH (i
) >= len
)
1553 if (! interval_has_some_properties_list (properties
, i
))
1557 if (BUFFERP (object
))
1558 signal_after_change (XINT (start
),
1559 XINT (end
) - XINT (start
),
1560 XINT (end
) - XINT (start
));
1566 else if (LENGTH (i
) == len
)
1568 if (!modified
&& BUFFERP (object
))
1569 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1570 remove_properties (Qnil
, properties
, i
, object
);
1571 if (BUFFERP (object
))
1572 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1573 XINT (end
) - XINT (start
));
1577 { /* i has the properties, and goes past the change limit. */
1579 i
= split_interval_left (i
, len
);
1580 copy_properties (unchanged
, i
);
1581 if (!modified
&& BUFFERP (object
))
1582 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1583 remove_properties (Qnil
, properties
, i
, object
);
1584 if (BUFFERP (object
))
1585 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1586 XINT (end
) - XINT (start
));
1590 if (interval_has_some_properties_list (properties
, i
))
1592 if (!modified
&& BUFFERP (object
))
1593 modify_region (XBUFFER (object
), XINT (start
), XINT (end
), 1);
1594 remove_properties (Qnil
, properties
, i
, object
);
1598 i
= next_interval (i
);
1602 DEFUN ("text-property-any", Ftext_property_any
,
1603 Stext_property_any
, 4, 5, 0,
1604 doc
: /* Check text from START to END for property PROPERTY equaling VALUE.
1605 If so, return the position of the first character whose property PROPERTY
1606 is `eq' to VALUE. Otherwise return nil.
1607 If the optional fifth argument OBJECT is a buffer (or nil, which means
1608 the current buffer), START and END are buffer positions (integers or
1609 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1610 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1612 register INTERVAL i
;
1613 register ptrdiff_t e
, pos
;
1616 XSETBUFFER (object
, current_buffer
);
1617 i
= validate_interval_range (object
, &start
, &end
, soft
);
1619 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1624 if (i
->position
>= e
)
1626 if (EQ (textget (i
->plist
, property
), value
))
1629 if (pos
< XINT (start
))
1631 return make_number (pos
);
1633 i
= next_interval (i
);
1638 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1639 Stext_property_not_all
, 4, 5, 0,
1640 doc
: /* Check text from START to END for property PROPERTY not equaling VALUE.
1641 If so, return the position of the first character whose property PROPERTY
1642 is not `eq' to VALUE. Otherwise, return nil.
1643 If the optional fifth argument OBJECT is a buffer (or nil, which means
1644 the current buffer), START and END are buffer positions (integers or
1645 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1646 (Lisp_Object start
, Lisp_Object end
, Lisp_Object property
, Lisp_Object value
, Lisp_Object object
)
1648 register INTERVAL i
;
1649 register ptrdiff_t s
, e
;
1652 XSETBUFFER (object
, current_buffer
);
1653 i
= validate_interval_range (object
, &start
, &end
, soft
);
1655 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1661 if (i
->position
>= e
)
1663 if (! EQ (textget (i
->plist
, property
), value
))
1665 if (i
->position
> s
)
1667 return make_number (s
);
1669 i
= next_interval (i
);
1675 /* Return the direction from which the text-property PROP would be
1676 inherited by any new text inserted at POS: 1 if it would be
1677 inherited from the char after POS, -1 if it would be inherited from
1678 the char before POS, and 0 if from neither.
1679 BUFFER can be either a buffer or nil (meaning current buffer). */
1682 text_property_stickiness (Lisp_Object prop
, Lisp_Object pos
, Lisp_Object buffer
)
1684 Lisp_Object prev_pos
, front_sticky
;
1685 int is_rear_sticky
= 1, is_front_sticky
= 0; /* defaults */
1686 Lisp_Object defalt
= Fassq (prop
, Vtext_property_default_nonsticky
);
1689 XSETBUFFER (buffer
, current_buffer
);
1691 if (CONSP (defalt
) && !NILP (XCDR (defalt
)))
1694 if (XINT (pos
) > BUF_BEGV (XBUFFER (buffer
)))
1695 /* Consider previous character. */
1697 Lisp_Object rear_non_sticky
;
1699 prev_pos
= make_number (XINT (pos
) - 1);
1700 rear_non_sticky
= Fget_text_property (prev_pos
, Qrear_nonsticky
, buffer
);
1702 if (!NILP (CONSP (rear_non_sticky
)
1703 ? Fmemq (prop
, rear_non_sticky
)
1705 /* PROP is rear-non-sticky. */
1711 /* Consider following character. */
1712 /* This signals an arg-out-of-range error if pos is outside the
1713 buffer's accessible range. */
1714 front_sticky
= Fget_text_property (pos
, Qfront_sticky
, buffer
);
1716 if (EQ (front_sticky
, Qt
)
1717 || (CONSP (front_sticky
)
1718 && !NILP (Fmemq (prop
, front_sticky
))))
1719 /* PROP is inherited from after. */
1720 is_front_sticky
= 1;
1722 /* Simple cases, where the properties are consistent. */
1723 if (is_rear_sticky
&& !is_front_sticky
)
1725 else if (!is_rear_sticky
&& is_front_sticky
)
1727 else if (!is_rear_sticky
&& !is_front_sticky
)
1730 /* The stickiness properties are inconsistent, so we have to
1731 disambiguate. Basically, rear-sticky wins, _except_ if the
1732 property that would be inherited has a value of nil, in which case
1733 front-sticky wins. */
1734 if (XINT (pos
) == BUF_BEGV (XBUFFER (buffer
))
1735 || NILP (Fget_text_property (prev_pos
, prop
, buffer
)))
1742 /* Copying properties between objects. */
1744 /* Add properties from START to END of SRC, starting at POS in DEST.
1745 SRC and DEST may each refer to strings or buffers.
1746 Optional sixth argument PROP causes only that property to be copied.
1747 Properties are copied to DEST as if by `add-text-properties'.
1748 Return t if any property value actually changed, nil otherwise. */
1750 /* Note this can GC when DEST is a buffer. */
1753 copy_text_properties (Lisp_Object start
, Lisp_Object end
, Lisp_Object src
, Lisp_Object pos
, Lisp_Object dest
, Lisp_Object prop
)
1759 ptrdiff_t s
, e
, e2
, p
, len
;
1761 struct gcpro gcpro1
, gcpro2
;
1763 i
= validate_interval_range (src
, &start
, &end
, soft
);
1767 CHECK_NUMBER_COERCE_MARKER (pos
);
1769 Lisp_Object dest_start
, dest_end
;
1771 e
= XINT (pos
) + (XINT (end
) - XINT (start
));
1772 if (MOST_POSITIVE_FIXNUM
< e
)
1773 args_out_of_range (pos
, end
);
1775 XSETFASTINT (dest_end
, e
);
1776 /* Apply this to a copy of pos; it will try to increment its arguments,
1777 which we don't want. */
1778 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1789 e2
= i
->position
+ LENGTH (i
);
1796 while (! NILP (plist
))
1798 if (EQ (Fcar (plist
), prop
))
1800 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1803 plist
= Fcdr (Fcdr (plist
));
1807 /* Must defer modifications to the interval tree in case src
1808 and dest refer to the same string or buffer. */
1809 stuff
= Fcons (Fcons (make_number (p
),
1810 Fcons (make_number (p
+ len
),
1811 Fcons (plist
, Qnil
))),
1815 i
= next_interval (i
);
1823 GCPRO2 (stuff
, dest
);
1825 while (! NILP (stuff
))
1828 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1829 Fcar (Fcdr (Fcdr (res
))), dest
);
1832 stuff
= Fcdr (stuff
);
1837 return modified
? Qt
: Qnil
;
1841 /* Return a list representing the text properties of OBJECT between
1842 START and END. if PROP is non-nil, report only on that property.
1843 Each result list element has the form (S E PLIST), where S and E
1844 are positions in OBJECT and PLIST is a property list containing the
1845 text properties of OBJECT between S and E. Value is nil if OBJECT
1846 doesn't contain text properties between START and END. */
1849 text_property_list (Lisp_Object object
, Lisp_Object start
, Lisp_Object end
, Lisp_Object prop
)
1856 i
= validate_interval_range (object
, &start
, &end
, soft
);
1859 ptrdiff_t s
= XINT (start
);
1860 ptrdiff_t e
= XINT (end
);
1864 ptrdiff_t interval_end
, len
;
1867 interval_end
= i
->position
+ LENGTH (i
);
1868 if (interval_end
> e
)
1870 len
= interval_end
- s
;
1875 for (; CONSP (plist
); plist
= Fcdr (XCDR (plist
)))
1876 if (EQ (XCAR (plist
), prop
))
1878 plist
= Fcons (prop
, Fcons (Fcar (XCDR (plist
)), Qnil
));
1883 result
= Fcons (Fcons (make_number (s
),
1884 Fcons (make_number (s
+ len
),
1885 Fcons (plist
, Qnil
))),
1888 i
= next_interval (i
);
1899 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1900 (START END PLIST), where START and END are positions and PLIST is a
1901 property list containing the text properties to add. Adjust START
1902 and END positions by DELTA before adding properties. Value is
1903 non-zero if OBJECT was modified. */
1906 add_text_properties_from_list (Lisp_Object object
, Lisp_Object list
, Lisp_Object delta
)
1908 struct gcpro gcpro1
, gcpro2
;
1911 GCPRO2 (list
, object
);
1913 for (; CONSP (list
); list
= XCDR (list
))
1915 Lisp_Object item
, start
, end
, plist
, tem
;
1918 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1919 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1920 plist
= XCAR (XCDR (XCDR (item
)));
1922 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1933 /* Modify end-points of ranges in LIST destructively, and return the
1934 new list. LIST is a list as returned from text_property_list.
1935 Discard properties that begin at or after NEW_END, and limit
1936 end-points to NEW_END. */
1939 extend_property_ranges (Lisp_Object list
, Lisp_Object new_end
)
1941 Lisp_Object prev
= Qnil
, head
= list
;
1942 ptrdiff_t max
= XINT (new_end
);
1944 for (; CONSP (list
); prev
= list
, list
= XCDR (list
))
1946 Lisp_Object item
, beg
, end
;
1950 end
= XCAR (XCDR (item
));
1952 if (XINT (beg
) >= max
)
1954 /* The start-point is past the end of the new string.
1955 Discard this property. */
1956 if (EQ (head
, list
))
1959 XSETCDR (prev
, XCDR (list
));
1961 else if (XINT (end
) > max
)
1962 /* The end-point is past the end of the new string. */
1963 XSETCAR (XCDR (item
), new_end
);
1971 /* Call the modification hook functions in LIST, each with START and END. */
1974 call_mod_hooks (Lisp_Object list
, Lisp_Object start
, Lisp_Object end
)
1976 struct gcpro gcpro1
;
1978 while (!NILP (list
))
1980 call2 (Fcar (list
), start
, end
);
1986 /* Check for read-only intervals between character positions START ... END,
1987 in BUF, and signal an error if we find one.
1989 Then check for any modification hooks in the range.
1990 Create a list of all these hooks in lexicographic order,
1991 eliminating consecutive extra copies of the same hook. Then call
1992 those hooks in order, with START and END - 1 as arguments. */
1995 verify_interval_modification (struct buffer
*buf
,
1996 ptrdiff_t start
, ptrdiff_t end
)
1998 INTERVAL intervals
= buffer_intervals (buf
);
2001 Lisp_Object prev_mod_hooks
;
2002 Lisp_Object mod_hooks
;
2003 struct gcpro gcpro1
;
2006 prev_mod_hooks
= Qnil
;
2009 interval_insert_behind_hooks
= Qnil
;
2010 interval_insert_in_front_hooks
= Qnil
;
2017 ptrdiff_t temp
= start
;
2022 /* For an insert operation, check the two chars around the position. */
2025 INTERVAL prev
= NULL
;
2026 Lisp_Object before
, after
;
2028 /* Set I to the interval containing the char after START,
2029 and PREV to the interval containing the char before START.
2030 Either one may be null. They may be equal. */
2031 i
= find_interval (intervals
, start
);
2033 if (start
== BUF_BEGV (buf
))
2035 else if (i
->position
== start
)
2036 prev
= previous_interval (i
);
2037 else if (i
->position
< start
)
2039 if (start
== BUF_ZV (buf
))
2042 /* If Vinhibit_read_only is set and is not a list, we can
2043 skip the read_only checks. */
2044 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
2046 /* If I and PREV differ we need to check for the read-only
2047 property together with its stickiness. If either I or
2048 PREV are 0, this check is all we need.
2049 We have to take special care, since read-only may be
2050 indirectly defined via the category property. */
2055 after
= textget (i
->plist
, Qread_only
);
2057 /* If interval I is read-only and read-only is
2058 front-sticky, inhibit insertion.
2059 Check for read-only as well as category. */
2061 && NILP (Fmemq (after
, Vinhibit_read_only
)))
2065 tem
= textget (i
->plist
, Qfront_sticky
);
2066 if (TMEM (Qread_only
, tem
)
2067 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2068 && TMEM (Qcategory
, tem
)))
2069 text_read_only (after
);
2075 before
= textget (prev
->plist
, Qread_only
);
2077 /* If interval PREV is read-only and read-only isn't
2078 rear-nonsticky, inhibit insertion.
2079 Check for read-only as well as category. */
2081 && NILP (Fmemq (before
, Vinhibit_read_only
)))
2085 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2086 if (! TMEM (Qread_only
, tem
)
2087 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
2088 || ! TMEM (Qcategory
, tem
)))
2089 text_read_only (before
);
2095 after
= textget (i
->plist
, Qread_only
);
2097 /* If interval I is read-only and read-only is
2098 front-sticky, inhibit insertion.
2099 Check for read-only as well as category. */
2100 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
2104 tem
= textget (i
->plist
, Qfront_sticky
);
2105 if (TMEM (Qread_only
, tem
)
2106 || (NILP (Fplist_get (i
->plist
, Qread_only
))
2107 && TMEM (Qcategory
, tem
)))
2108 text_read_only (after
);
2110 tem
= textget (prev
->plist
, Qrear_nonsticky
);
2111 if (! TMEM (Qread_only
, tem
)
2112 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
2113 || ! TMEM (Qcategory
, tem
)))
2114 text_read_only (after
);
2119 /* Run both insert hooks (just once if they're the same). */
2121 interval_insert_behind_hooks
2122 = textget (prev
->plist
, Qinsert_behind_hooks
);
2124 interval_insert_in_front_hooks
2125 = textget (i
->plist
, Qinsert_in_front_hooks
);
2129 /* Loop over intervals on or next to START...END,
2130 collecting their hooks. */
2132 i
= find_interval (intervals
, start
);
2135 if (! INTERVAL_WRITABLE_P (i
))
2136 text_read_only (textget (i
->plist
, Qread_only
));
2138 if (!inhibit_modification_hooks
)
2140 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
2141 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
2143 hooks
= Fcons (mod_hooks
, hooks
);
2144 prev_mod_hooks
= mod_hooks
;
2148 i
= next_interval (i
);
2150 /* Keep going thru the interval containing the char before END. */
2151 while (i
&& i
->position
< end
);
2153 if (!inhibit_modification_hooks
)
2156 hooks
= Fnreverse (hooks
);
2157 while (! EQ (hooks
, Qnil
))
2159 call_mod_hooks (Fcar (hooks
), make_number (start
),
2161 hooks
= Fcdr (hooks
);
2168 /* Run the interval hooks for an insertion on character range START ... END.
2169 verify_interval_modification chose which hooks to run;
2170 this function is called after the insertion happens
2171 so it can indicate the range of inserted text. */
2174 report_interval_modification (Lisp_Object start
, Lisp_Object end
)
2176 if (! NILP (interval_insert_behind_hooks
))
2177 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
2178 if (! NILP (interval_insert_in_front_hooks
)
2179 && ! EQ (interval_insert_in_front_hooks
,
2180 interval_insert_behind_hooks
))
2181 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
2185 syms_of_textprop (void)
2187 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties
,
2188 doc
: /* Property-list used as default values.
2189 The value of a property in this list is seen as the value for every
2190 character that does not have its own value for that property. */);
2191 Vdefault_text_properties
= Qnil
;
2193 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist
,
2194 doc
: /* Alist of alternative properties for properties without a value.
2195 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2196 If a piece of text has no direct value for a particular property, then
2197 this alist is consulted. If that property appears in the alist, then
2198 the first non-nil value from the associated alternative properties is
2200 Vchar_property_alias_alist
= Qnil
;
2202 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks
,
2203 doc
: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2204 This also inhibits the use of the `intangible' text property. */);
2205 Vinhibit_point_motion_hooks
= Qnil
;
2207 DEFVAR_LISP ("text-property-default-nonsticky",
2208 Vtext_property_default_nonsticky
,
2209 doc
: /* Alist of properties vs the corresponding non-stickiness.
2210 Each element has the form (PROPERTY . NONSTICKINESS).
2212 If a character in a buffer has PROPERTY, new text inserted adjacent to
2213 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2214 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2215 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2216 /* Text properties `syntax-table'and `display' should be nonsticky
2218 Vtext_property_default_nonsticky
2219 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt
),
2220 Fcons (Fcons (intern_c_string ("display"), Qt
), Qnil
));
2222 staticpro (&interval_insert_behind_hooks
);
2223 staticpro (&interval_insert_in_front_hooks
);
2224 interval_insert_behind_hooks
= Qnil
;
2225 interval_insert_in_front_hooks
= Qnil
;
2228 /* Common attributes one might give text */
2230 DEFSYM (Qforeground
, "foreground");
2231 DEFSYM (Qbackground
, "background");
2232 DEFSYM (Qfont
, "font");
2233 DEFSYM (Qstipple
, "stipple");
2234 DEFSYM (Qunderline
, "underline");
2235 DEFSYM (Qread_only
, "read-only");
2236 DEFSYM (Qinvisible
, "invisible");
2237 DEFSYM (Qintangible
, "intangible");
2238 DEFSYM (Qcategory
, "category");
2239 DEFSYM (Qlocal_map
, "local-map");
2240 DEFSYM (Qfront_sticky
, "front-sticky");
2241 DEFSYM (Qrear_nonsticky
, "rear-nonsticky");
2242 DEFSYM (Qmouse_face
, "mouse-face");
2243 DEFSYM (Qminibuffer_prompt
, "minibuffer-prompt");
2245 /* Properties that text might use to specify certain actions */
2247 DEFSYM (Qmouse_left
, "mouse-left");
2248 DEFSYM (Qmouse_entered
, "mouse-entered");
2249 DEFSYM (Qpoint_left
, "point-left");
2250 DEFSYM (Qpoint_entered
, "point-entered");
2252 defsubr (&Stext_properties_at
);
2253 defsubr (&Sget_text_property
);
2254 defsubr (&Sget_char_property
);
2255 defsubr (&Sget_char_property_and_overlay
);
2256 defsubr (&Snext_char_property_change
);
2257 defsubr (&Sprevious_char_property_change
);
2258 defsubr (&Snext_single_char_property_change
);
2259 defsubr (&Sprevious_single_char_property_change
);
2260 defsubr (&Snext_property_change
);
2261 defsubr (&Snext_single_property_change
);
2262 defsubr (&Sprevious_property_change
);
2263 defsubr (&Sprevious_single_property_change
);
2264 defsubr (&Sadd_text_properties
);
2265 defsubr (&Sput_text_property
);
2266 defsubr (&Sset_text_properties
);
2267 defsubr (&Sremove_text_properties
);
2268 defsubr (&Sremove_list_of_text_properties
);
2269 defsubr (&Stext_property_any
);
2270 defsubr (&Stext_property_not_all
);