1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993 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 2, or (at your option)
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; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include "intervals.h"
26 /* NOTES: previous- and next- property change will have to skip
27 zero-length intervals if they are implemented. This could be done
28 inside next_interval and previous_interval.
30 set_properties needs to deal with the interval property cache.
32 It is assumed that for any interval plist, a property appears
33 only once on the list. Although some code i.e., remove_properties,
34 handles the more general case, the uniqueness of properties is
35 necessary for the system to remain consistent. This requirement
36 is enforced by the subrs installing properties onto the intervals. */
38 /* The rest of the file is within this conditional */
39 #ifdef USE_TEXT_PROPERTIES
42 Lisp_Object Qmouse_left
;
43 Lisp_Object Qmouse_entered
;
44 Lisp_Object Qpoint_left
;
45 Lisp_Object Qpoint_entered
;
46 Lisp_Object Qcategory
;
47 Lisp_Object Qlocal_map
;
49 /* Visual properties text (including strings) may have. */
50 Lisp_Object Qforeground
, Qbackground
, Qfont
, Qunderline
, Qstipple
;
51 Lisp_Object Qinvisible
, Qread_only
, Qhidden
;
53 /* Sticky properties */
54 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
56 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
57 the o1's cdr. Otherwise, return zero. This is handy for
59 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && CONSP ((o2) = XCONS (o1)->cdr))
61 Lisp_Object Vinhibit_point_motion_hooks
;
64 /* Extract the interval at the position pointed to by BEGIN from
65 OBJECT, a string or buffer. Additionally, check that the positions
66 pointed to by BEGIN and END are within the bounds of OBJECT, and
67 reverse them if *BEGIN is greater than *END. The objects pointed
68 to by BEGIN and END may be integers or markers; if the latter, they
69 are coerced to integers.
71 When OBJECT is a string, we increment *BEGIN and *END
72 to make them origin-one.
74 Note that buffer points don't correspond to interval indices.
75 For example, point-max is 1 greater than the index of the last
76 character. This difference is handled in the caller, which uses
77 the validated points to determine a length, and operates on that.
78 Exceptions are Ftext_properties_at, Fnext_property_change, and
79 Fprevious_property_change which call this function with BEGIN == END.
80 Handle this case specially.
82 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
83 create an interval tree for OBJECT if one doesn't exist, provided
84 the object actually contains text. In the current design, if there
85 is no text, there can be no text properties. */
91 validate_interval_range (object
, begin
, end
, force
)
92 Lisp_Object object
, *begin
, *end
;
98 CHECK_STRING_OR_BUFFER (object
, 0);
99 CHECK_NUMBER_COERCE_MARKER (*begin
, 0);
100 CHECK_NUMBER_COERCE_MARKER (*end
, 0);
102 /* If we are asked for a point, but from a subr which operates
103 on a range, then return nothing. */
104 if (*begin
== *end
&& begin
!= end
)
105 return NULL_INTERVAL
;
107 if (XINT (*begin
) > XINT (*end
))
115 if (XTYPE (object
) == Lisp_Buffer
)
117 register struct buffer
*b
= XBUFFER (object
);
119 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
120 && XINT (*end
) <= BUF_ZV (b
)))
121 args_out_of_range (*begin
, *end
);
124 /* If there's no text, there are no properties. */
125 if (BUF_BEGV (b
) == BUF_ZV (b
))
126 return NULL_INTERVAL
;
128 searchpos
= XINT (*begin
);
132 register struct Lisp_String
*s
= XSTRING (object
);
134 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
135 && XINT (*end
) <= s
->size
))
136 args_out_of_range (*begin
, *end
);
137 /* User-level Positions in strings start with 0,
138 but the interval code always wants positions starting with 1. */
139 XFASTINT (*begin
) += 1;
141 XFASTINT (*end
) += 1;
145 return NULL_INTERVAL
;
147 searchpos
= XINT (*begin
);
150 if (NULL_INTERVAL_P (i
))
151 return (force
? create_root_interval (object
) : i
);
153 return find_interval (i
, searchpos
);
156 /* Validate LIST as a property list. If LIST is not a list, then
157 make one consisting of (LIST nil). Otherwise, verify that LIST
158 is even numbered and thus suitable as a plist. */
161 validate_plist (list
)
170 register Lisp_Object tail
;
171 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
177 error ("Odd length text property list");
181 return Fcons (list
, Fcons (Qnil
, Qnil
));
184 /* Return nonzero if interval I has all the properties,
185 with the same values, of list PLIST. */
188 interval_has_all_properties (plist
, i
)
192 register Lisp_Object tail1
, tail2
, sym1
, sym2
;
195 /* Go through each element of PLIST. */
196 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
201 /* Go through I's plist, looking for sym1 */
202 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
203 if (EQ (sym1
, Fcar (tail2
)))
205 /* Found the same property on both lists. If the
206 values are unequal, return zero. */
207 if (! EQ (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))))
210 /* Property has same value on both lists; go to next one. */
222 /* Return nonzero if the plist of interval I has any of the
223 properties of PLIST, regardless of their values. */
226 interval_has_some_properties (plist
, i
)
230 register Lisp_Object tail1
, tail2
, sym
;
232 /* Go through each element of PLIST. */
233 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
237 /* Go through i's plist, looking for tail1 */
238 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
239 if (EQ (sym
, Fcar (tail2
)))
246 /* Changing the plists of individual intervals. */
248 /* Return the value of PROP in property-list PLIST, or Qunbound if it
251 property_value (plist
, prop
)
255 while (PLIST_ELT_P (plist
, value
))
256 if (EQ (XCONS (plist
)->car
, prop
))
257 return XCONS (value
)->car
;
259 plist
= XCONS (value
)->cdr
;
264 /* Set the properties of INTERVAL to PROPERTIES,
265 and record undo info for the previous values.
266 OBJECT is the string or buffer that INTERVAL belongs to. */
269 set_properties (properties
, interval
, object
)
270 Lisp_Object properties
, object
;
273 Lisp_Object sym
, value
;
275 if (BUFFERP (object
))
277 /* For each property in the old plist which is missing from PROPERTIES,
278 or has a different value in PROPERTIES, make an undo record. */
279 for (sym
= interval
->plist
;
280 PLIST_ELT_P (sym
, value
);
281 sym
= XCONS (value
)->cdr
)
282 if (! EQ (property_value (properties
, XCONS (sym
)->car
),
285 modify_region (XBUFFER (object
),
286 make_number (interval
->position
),
287 make_number (interval
->position
+ LENGTH (interval
)));
288 record_property_change (interval
->position
, LENGTH (interval
),
289 XCONS (sym
)->car
, XCONS (value
)->car
,
293 /* For each new property that has no value at all in the old plist,
294 make an undo record binding it to nil, so it will be removed. */
295 for (sym
= properties
;
296 PLIST_ELT_P (sym
, value
);
297 sym
= XCONS (value
)->cdr
)
298 if (EQ (property_value (interval
->plist
, XCONS (sym
)->car
), Qunbound
))
300 modify_region (XBUFFER (object
),
301 make_number (interval
->position
),
302 make_number (interval
->position
+ LENGTH (interval
)));
303 record_property_change (interval
->position
, LENGTH (interval
),
304 XCONS (sym
)->car
, Qnil
,
309 /* Store new properties. */
310 interval
->plist
= Fcopy_sequence (properties
);
313 /* Add the properties of PLIST to the interval I, or set
314 the value of I's property to the value of the property on PLIST
315 if they are different.
317 OBJECT should be the string or buffer the interval is in.
319 Return nonzero if this changes I (i.e., if any members of PLIST
320 are actually added to I's plist) */
323 add_properties (plist
, i
, object
)
328 register Lisp_Object tail1
, tail2
, sym1
, val1
;
329 register int changed
= 0;
332 /* Go through each element of PLIST. */
333 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
336 val1
= Fcar (Fcdr (tail1
));
339 /* Go through I's plist, looking for sym1 */
340 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
341 if (EQ (sym1
, Fcar (tail2
)))
343 register Lisp_Object this_cdr
= Fcdr (tail2
);
345 /* Found the property. Now check its value. */
348 /* The properties have the same value on both lists.
349 Continue to the next property. */
350 if (EQ (val1
, Fcar (this_cdr
)))
353 /* Record this change in the buffer, for undo purposes. */
354 if (XTYPE (object
) == Lisp_Buffer
)
356 modify_region (XBUFFER (object
),
357 make_number (i
->position
),
358 make_number (i
->position
+ LENGTH (i
)));
359 record_property_change (i
->position
, LENGTH (i
),
360 sym1
, Fcar (this_cdr
), object
);
363 /* I's property has a different value -- change it */
364 Fsetcar (this_cdr
, val1
);
371 /* Record this change in the buffer, for undo purposes. */
372 if (XTYPE (object
) == Lisp_Buffer
)
374 modify_region (XBUFFER (object
),
375 make_number (i
->position
),
376 make_number (i
->position
+ LENGTH (i
)));
377 record_property_change (i
->position
, LENGTH (i
),
380 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
388 /* For any members of PLIST which are properties of I, remove them
390 OBJECT is the string or buffer containing I. */
393 remove_properties (plist
, i
, object
)
398 register Lisp_Object tail1
, tail2
, sym
;
399 register Lisp_Object current_plist
= i
->plist
;
400 register int changed
= 0;
402 /* Go through each element of plist. */
403 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
407 /* First, remove the symbol if its at the head of the list */
408 while (! NILP (current_plist
) && EQ (sym
, Fcar (current_plist
)))
410 if (XTYPE (object
) == Lisp_Buffer
)
412 modify_region (XBUFFER (object
),
413 make_number (i
->position
),
414 make_number (i
->position
+ LENGTH (i
)));
415 record_property_change (i
->position
, LENGTH (i
),
416 sym
, Fcar (Fcdr (current_plist
)),
420 current_plist
= Fcdr (Fcdr (current_plist
));
424 /* Go through i's plist, looking for sym */
425 tail2
= current_plist
;
426 while (! NILP (tail2
))
428 register Lisp_Object
this = Fcdr (Fcdr (tail2
));
429 if (EQ (sym
, Fcar (this)))
431 if (XTYPE (object
) == Lisp_Buffer
)
433 modify_region (XBUFFER (object
),
434 make_number (i
->position
),
435 make_number (i
->position
+ LENGTH (i
)));
436 record_property_change (i
->position
, LENGTH (i
),
437 sym
, Fcar (Fcdr (this)), object
);
440 Fsetcdr (Fcdr (tail2
), Fcdr (Fcdr (this)));
448 i
->plist
= current_plist
;
453 /* Remove all properties from interval I. Return non-zero
454 if this changes the interval. */
468 DEFUN ("text-properties-at", Ftext_properties_at
,
469 Stext_properties_at
, 1, 2, 0,
470 "Return the list of properties held by the character at POSITION\n\
471 in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
472 defaults to the current buffer.\n\
473 If POSITION is at the end of OBJECT, the value is nil.")
475 Lisp_Object pos
, object
;
480 XSET (object
, Lisp_Buffer
, current_buffer
);
482 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
483 if (NULL_INTERVAL_P (i
))
485 /* If POS is at the end of the interval,
486 it means it's the end of OBJECT.
487 There are no properties at the very end,
488 since no character follows. */
489 if (XINT (pos
) == LENGTH (i
) + i
->position
)
495 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
496 "Return the value of position POS's property PROP, in OBJECT.\n\
497 OBJECT is optional and defaults to the current buffer.\n\
498 If POSITION is at the end of OBJECT, the value is nil.")
500 Lisp_Object pos
, object
;
501 register Lisp_Object prop
;
504 register Lisp_Object tail
;
507 XSET (object
, Lisp_Buffer
, current_buffer
);
508 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
509 if (NULL_INTERVAL_P (i
))
512 /* If POS is at the end of the interval,
513 it means it's the end of OBJECT.
514 There are no properties at the very end,
515 since no character follows. */
516 if (XINT (pos
) == LENGTH (i
) + i
->position
)
519 return textget (i
->plist
, prop
);
522 DEFUN ("next-property-change", Fnext_property_change
,
523 Snext_property_change
, 1, 3, 0,
524 "Return the position of next property change.\n\
525 Scans characters forward from POS in OBJECT till it finds\n\
526 a change in some text property, then returns the position of the change.\n\
527 The optional second argument OBJECT is the string or buffer to scan.\n\
528 Return nil if the property is constant all the way to the end of OBJECT.\n\
529 If the value is non-nil, it is a position greater than POS, never equal.\n\n\
530 If the optional third argument LIMIT is non-nil, don't search\n\
531 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
533 Lisp_Object pos
, object
, limit
;
535 register INTERVAL i
, next
;
538 XSET (object
, Lisp_Buffer
, current_buffer
);
540 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
541 if (NULL_INTERVAL_P (i
))
544 next
= next_interval (i
);
545 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
546 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
547 next
= next_interval (next
);
549 if (NULL_INTERVAL_P (next
))
551 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
554 return next
->position
- (XTYPE (object
) == Lisp_String
);
557 /* Return 1 if there's a change in some property between BEG and END. */
560 property_change_between_p (beg
, end
)
563 register INTERVAL i
, next
;
564 Lisp_Object object
, pos
;
566 XSET (object
, Lisp_Buffer
, current_buffer
);
567 XFASTINT (pos
) = beg
;
569 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
570 if (NULL_INTERVAL_P (i
))
573 next
= next_interval (i
);
574 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
576 next
= next_interval (next
);
577 if (NULL_INTERVAL_P (next
))
579 if (next
->position
>= end
)
583 if (NULL_INTERVAL_P (next
))
589 DEFUN ("next-single-property-change", Fnext_single_property_change
,
590 Snext_single_property_change
, 2, 4, 0,
591 "Return the position of next property change for a specific property.\n\
592 Scans characters forward from POS till it finds\n\
593 a change in the PROP property, then returns the position of the change.\n\
594 The optional third argument OBJECT is the string or buffer to scan.\n\
595 The property values are compared with `eq'.\n\
596 Return nil if the property is constant all the way to the end of OBJECT.\n\
597 If the value is non-nil, it is a position greater than POS, never equal.\n\n\
598 If the optional fourth argument LIMIT is non-nil, don't search\n\
599 past position LIMIT; fail if nothing is found before LIMIT.")
600 (pos
, prop
, object
, limit
)
601 Lisp_Object pos
, prop
, object
, limit
;
603 register INTERVAL i
, next
;
604 register Lisp_Object here_val
;
607 XSET (object
, Lisp_Buffer
, current_buffer
);
609 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
610 if (NULL_INTERVAL_P (i
))
613 here_val
= textget (i
->plist
, prop
);
614 next
= next_interval (i
);
615 while (! NULL_INTERVAL_P (next
)
616 && EQ (here_val
, textget (next
->plist
, prop
))
617 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
618 next
= next_interval (next
);
620 if (NULL_INTERVAL_P (next
))
622 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
625 return next
->position
- (XTYPE (object
) == Lisp_String
);
628 DEFUN ("previous-property-change", Fprevious_property_change
,
629 Sprevious_property_change
, 1, 3, 0,
630 "Return the position of previous property change.\n\
631 Scans characters backwards from POS in OBJECT till it finds\n\
632 a change in some text property, then returns the position of the change.\n\
633 The optional second argument OBJECT is the string or buffer to scan.\n\
634 Return nil if the property is constant all the way to the start of OBJECT.\n\
635 If the value is non-nil, it is a position less than POS, never equal.\n\n\
636 If the optional third argument LIMIT is non-nil, don't search\n\
637 back past position LIMIT; fail if nothing is found before LIMIT.")
639 Lisp_Object pos
, object
, limit
;
641 register INTERVAL i
, previous
;
644 XSET (object
, Lisp_Buffer
, current_buffer
);
646 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
647 if (NULL_INTERVAL_P (i
))
650 previous
= previous_interval (i
);
651 while (! NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
653 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
654 previous
= previous_interval (previous
);
655 if (NULL_INTERVAL_P (previous
))
658 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
661 return (previous
->position
+ LENGTH (previous
)
662 - (XTYPE (object
) == Lisp_String
));
665 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
666 Sprevious_single_property_change
, 2, 4, 0,
667 "Return the position of previous property change for a specific property.\n\
668 Scans characters backward from POS till it finds\n\
669 a change in the PROP property, then returns the position of the change.\n\
670 The optional third argument OBJECT is the string or buffer to scan.\n\
671 The property values are compared with `eq'.\n\
672 Return nil if the property is constant all the way to the start of OBJECT.\n\
673 If the value is non-nil, it is a position less than POS, never equal.\n\n\
674 If the optional fourth argument LIMIT is non-nil, don't search\n\
675 back past position LIMIT; fail if nothing is found before LIMIT.")
676 (pos
, prop
, object
, limit
)
677 Lisp_Object pos
, prop
, object
, limit
;
679 register INTERVAL i
, previous
;
680 register Lisp_Object here_val
;
683 XSET (object
, Lisp_Buffer
, current_buffer
);
685 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
686 if (NULL_INTERVAL_P (i
))
689 here_val
= textget (i
->plist
, prop
);
690 previous
= previous_interval (i
);
691 while (! NULL_INTERVAL_P (previous
)
692 && EQ (here_val
, textget (previous
->plist
, prop
))
694 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
695 previous
= previous_interval (previous
);
696 if (NULL_INTERVAL_P (previous
))
699 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
702 return (previous
->position
+ LENGTH (previous
)
703 - (XTYPE (object
) == Lisp_String
));
706 DEFUN ("add-text-properties", Fadd_text_properties
,
707 Sadd_text_properties
, 3, 4, 0,
708 "Add properties to the text from START to END.\n\
709 The third argument PROPS is a property list\n\
710 specifying the property values to add.\n\
711 The optional fourth argument, OBJECT,\n\
712 is the string or buffer containing the text.\n\
713 Return t if any property value actually changed, nil otherwise.")
714 (start
, end
, properties
, object
)
715 Lisp_Object start
, end
, properties
, object
;
717 register INTERVAL i
, unchanged
;
718 register int s
, len
, modified
= 0;
720 properties
= validate_plist (properties
);
721 if (NILP (properties
))
725 XSET (object
, Lisp_Buffer
, current_buffer
);
727 i
= validate_interval_range (object
, &start
, &end
, hard
);
728 if (NULL_INTERVAL_P (i
))
732 len
= XINT (end
) - s
;
734 /* If we're not starting on an interval boundary, we have to
735 split this interval. */
736 if (i
->position
!= s
)
738 /* If this interval already has the properties, we can
740 if (interval_has_all_properties (properties
, i
))
742 int got
= (LENGTH (i
) - (s
- i
->position
));
746 i
= next_interval (i
);
751 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
752 copy_properties (unchanged
, i
);
756 /* We are at the beginning of interval I, with LEN chars to scan. */
762 if (LENGTH (i
) >= len
)
764 if (interval_has_all_properties (properties
, i
))
765 return modified
? Qt
: Qnil
;
767 if (LENGTH (i
) == len
)
769 add_properties (properties
, i
, object
);
773 /* i doesn't have the properties, and goes past the change limit */
775 i
= split_interval_left (unchanged
, len
);
776 copy_properties (unchanged
, i
);
777 add_properties (properties
, i
, object
);
782 modified
+= add_properties (properties
, i
, object
);
783 i
= next_interval (i
);
787 DEFUN ("put-text-property", Fput_text_property
,
788 Sput_text_property
, 4, 5, 0,
789 "Set one property of the text from START to END.\n\
790 The third and fourth arguments PROP and VALUE\n\
791 specify the property to add.\n\
792 The optional fifth argument, OBJECT,\n\
793 is the string or buffer containing the text.")
794 (start
, end
, prop
, value
, object
)
795 Lisp_Object start
, end
, prop
, value
, object
;
797 Fadd_text_properties (start
, end
,
798 Fcons (prop
, Fcons (value
, Qnil
)),
803 DEFUN ("set-text-properties", Fset_text_properties
,
804 Sset_text_properties
, 3, 4, 0,
805 "Completely replace properties of text from START to END.\n\
806 The third argument PROPS is the new property list.\n\
807 The optional fourth argument, OBJECT,\n\
808 is the string or buffer containing the text.")
809 (start
, end
, props
, object
)
810 Lisp_Object start
, end
, props
, object
;
812 register INTERVAL i
, unchanged
;
813 register INTERVAL prev_changed
= NULL_INTERVAL
;
816 props
= validate_plist (props
);
819 XSET (object
, Lisp_Buffer
, current_buffer
);
821 i
= validate_interval_range (object
, &start
, &end
, hard
);
822 if (NULL_INTERVAL_P (i
))
826 len
= XINT (end
) - s
;
828 if (i
->position
!= s
)
831 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
833 if (LENGTH (i
) > len
)
835 copy_properties (unchanged
, i
);
836 i
= split_interval_left (i
, len
);
837 set_properties (props
, i
, object
);
841 set_properties (props
, i
, object
);
843 if (LENGTH (i
) == len
)
848 i
= next_interval (i
);
851 /* We are starting at the beginning of an interval, I */
857 if (LENGTH (i
) >= len
)
859 if (LENGTH (i
) > len
)
860 i
= split_interval_left (i
, len
);
862 if (NULL_INTERVAL_P (prev_changed
))
863 set_properties (props
, i
, object
);
865 merge_interval_left (i
);
870 if (NULL_INTERVAL_P (prev_changed
))
872 set_properties (props
, i
, object
);
876 prev_changed
= i
= merge_interval_left (i
);
878 i
= next_interval (i
);
884 DEFUN ("remove-text-properties", Fremove_text_properties
,
885 Sremove_text_properties
, 3, 4, 0,
886 "Remove some properties from text from START to END.\n\
887 The third argument PROPS is a property list\n\
888 whose property names specify the properties to remove.\n\
889 \(The values stored in PROPS are ignored.)\n\
890 The optional fourth argument, OBJECT,\n\
891 is the string or buffer containing the text.\n\
892 Return t if any property was actually removed, nil otherwise.")
893 (start
, end
, props
, object
)
894 Lisp_Object start
, end
, props
, object
;
896 register INTERVAL i
, unchanged
;
897 register int s
, len
, modified
= 0;
900 XSET (object
, Lisp_Buffer
, current_buffer
);
902 i
= validate_interval_range (object
, &start
, &end
, soft
);
903 if (NULL_INTERVAL_P (i
))
907 len
= XINT (end
) - s
;
909 if (i
->position
!= s
)
911 /* No properties on this first interval -- return if
912 it covers the entire region. */
913 if (! interval_has_some_properties (props
, i
))
915 int got
= (LENGTH (i
) - (s
- i
->position
));
919 i
= next_interval (i
);
921 /* Split away the beginning of this interval; what we don't
926 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
927 copy_properties (unchanged
, i
);
931 /* We are at the beginning of an interval, with len to scan */
937 if (LENGTH (i
) >= len
)
939 if (! interval_has_some_properties (props
, i
))
940 return modified
? Qt
: Qnil
;
942 if (LENGTH (i
) == len
)
944 remove_properties (props
, i
, object
);
948 /* i has the properties, and goes past the change limit */
950 i
= split_interval_left (i
, len
);
951 copy_properties (unchanged
, i
);
952 remove_properties (props
, i
, object
);
957 modified
+= remove_properties (props
, i
, object
);
958 i
= next_interval (i
);
962 DEFUN ("text-property-any", Ftext_property_any
,
963 Stext_property_any
, 4, 5, 0,
964 "Check text from START to END to see if PROP is ever `eq' to VALUE.\n\
965 If so, return the position of the first character whose PROP is `eq'\n\
966 to VALUE. Otherwise return nil.\n\
967 The optional fifth argument, OBJECT, is the string or buffer\n\
968 containing the text.")
969 (start
, end
, prop
, value
, object
)
970 Lisp_Object start
, end
, prop
, value
, object
;
976 XSET (object
, Lisp_Buffer
, current_buffer
);
977 i
= validate_interval_range (object
, &start
, &end
, soft
);
980 while (! NULL_INTERVAL_P (i
))
982 if (i
->position
>= e
)
984 if (EQ (textget (i
->plist
, prop
), value
))
987 if (pos
< XINT (start
))
989 return make_number (pos
- (XTYPE (object
) == Lisp_String
));
991 i
= next_interval (i
);
996 DEFUN ("text-property-not-all", Ftext_property_not_all
,
997 Stext_property_not_all
, 4, 5, 0,
998 "Check text from START to END to see if PROP is ever not `eq' to VALUE.\n\
999 If so, return the position of the first character whose PROP is not\n\
1000 `eq' to VALUE. Otherwise, return nil.\n\
1001 The optional fifth argument, OBJECT, is the string or buffer\n\
1002 containing the text.")
1003 (start
, end
, prop
, value
, object
)
1004 Lisp_Object start
, end
, prop
, value
, object
;
1006 register INTERVAL i
;
1010 XSET (object
, Lisp_Buffer
, current_buffer
);
1011 i
= validate_interval_range (object
, &start
, &end
, soft
);
1012 if (NULL_INTERVAL_P (i
))
1013 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1017 while (! NULL_INTERVAL_P (i
))
1019 if (i
->position
>= e
)
1021 if (! EQ (textget (i
->plist
, prop
), value
))
1023 if (i
->position
> s
)
1025 return make_number (s
- (XTYPE (object
) == Lisp_String
));
1027 i
= next_interval (i
);
1032 #if 0 /* You can use set-text-properties for this. */
1034 DEFUN ("erase-text-properties", Ferase_text_properties
,
1035 Serase_text_properties
, 2, 3, 0,
1036 "Remove all properties from the text from START to END.\n\
1037 The optional third argument, OBJECT,\n\
1038 is the string or buffer containing the text.")
1039 (start
, end
, object
)
1040 Lisp_Object start
, end
, object
;
1042 register INTERVAL i
;
1043 register INTERVAL prev_changed
= NULL_INTERVAL
;
1044 register int s
, len
, modified
;
1047 XSET (object
, Lisp_Buffer
, current_buffer
);
1049 i
= validate_interval_range (object
, &start
, &end
, soft
);
1050 if (NULL_INTERVAL_P (i
))
1054 len
= XINT (end
) - s
;
1056 if (i
->position
!= s
)
1059 register INTERVAL unchanged
= i
;
1061 /* If there are properties here, then this text will be modified. */
1062 if (! NILP (i
->plist
))
1064 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1068 if (LENGTH (i
) > len
)
1070 i
= split_interval_right (i
, len
);
1071 copy_properties (unchanged
, i
);
1075 if (LENGTH (i
) == len
)
1080 /* If the text of I is without any properties, and contains
1081 LEN or more characters, then we may return without changing
1083 else if (LENGTH (i
) - (s
- i
->position
) <= len
)
1085 /* The amount of text to change extends past I, so just note
1086 how much we've gotten. */
1088 got
= LENGTH (i
) - (s
- i
->position
);
1092 i
= next_interval (i
);
1095 /* We are starting at the beginning of an interval, I. */
1098 if (LENGTH (i
) >= len
)
1100 /* If I has no properties, simply merge it if possible. */
1101 if (NILP (i
->plist
))
1103 if (! NULL_INTERVAL_P (prev_changed
))
1104 merge_interval_left (i
);
1106 return modified
? Qt
: Qnil
;
1109 if (LENGTH (i
) > len
)
1110 i
= split_interval_left (i
, len
);
1111 if (! NULL_INTERVAL_P (prev_changed
))
1112 merge_interval_left (i
);
1119 /* Here if we still need to erase past the end of I */
1121 if (NULL_INTERVAL_P (prev_changed
))
1123 modified
+= erase_properties (i
);
1128 modified
+= ! NILP (i
->plist
);
1129 /* Merging I will give it the properties of PREV_CHANGED. */
1130 prev_changed
= i
= merge_interval_left (i
);
1133 i
= next_interval (i
);
1136 return modified
? Qt
: Qnil
;
1140 /* I don't think this is the right interface to export; how often do you
1141 want to do something like this, other than when you're copying objects
1144 I think it would be better to have a pair of functions, one which
1145 returns the text properties of a region as a list of ranges and
1146 plists, and another which applies such a list to another object. */
1148 /* DEFUN ("copy-text-properties", Fcopy_text_properties,
1149 Scopy_text_properties, 5, 6, 0,
1150 "Add properties from SRC-START to SRC-END of SRC at DEST-POS of DEST.\n\
1151 SRC and DEST may each refer to strings or buffers.\n\
1152 Optional sixth argument PROP causes only that property to be copied.\n\
1153 Properties are copied to DEST as if by `add-text-properties'.\n\
1154 Return t if any property value actually changed, nil otherwise.") */
1157 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1158 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1164 int s
, e
, e2
, p
, len
, modified
= 0;
1166 i
= validate_interval_range (src
, &start
, &end
, soft
);
1167 if (NULL_INTERVAL_P (i
))
1170 CHECK_NUMBER_COERCE_MARKER (pos
, 0);
1172 Lisp_Object dest_start
, dest_end
;
1175 XFASTINT (dest_end
) = XINT (dest_start
) + (XINT (end
) - XINT (start
));
1176 /* Apply this to a copy of pos; it will try to increment its arguments,
1177 which we don't want. */
1178 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1189 e2
= i
->position
+ LENGTH (i
);
1196 while (! NILP (plist
))
1198 if (EQ (Fcar (plist
), prop
))
1200 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1203 plist
= Fcdr (Fcdr (plist
));
1207 /* Must defer modifications to the interval tree in case src
1208 and dest refer to the same string or buffer. */
1209 stuff
= Fcons (Fcons (make_number (p
),
1210 Fcons (make_number (p
+ len
),
1211 Fcons (plist
, Qnil
))),
1215 i
= next_interval (i
);
1216 if (NULL_INTERVAL_P (i
))
1223 while (! NILP (stuff
))
1226 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1227 Fcar (Fcdr (Fcdr (res
))), dest
);
1230 stuff
= Fcdr (stuff
);
1233 return modified
? Qt
: Qnil
;
1239 DEFVAR_INT ("interval-balance-threshold", &interval_balance_threshold
,
1240 "Threshold for rebalancing interval trees, expressed as the\n\
1241 percentage by which the left interval tree should not differ from the right.");
1242 interval_balance_threshold
= 8;
1244 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
1245 "If nonnil, don't call the text property values of\n\
1246 `point-left' and `point-entered'.");
1247 Vinhibit_point_motion_hooks
= Qnil
;
1249 /* Common attributes one might give text */
1251 staticpro (&Qforeground
);
1252 Qforeground
= intern ("foreground");
1253 staticpro (&Qbackground
);
1254 Qbackground
= intern ("background");
1256 Qfont
= intern ("font");
1257 staticpro (&Qstipple
);
1258 Qstipple
= intern ("stipple");
1259 staticpro (&Qunderline
);
1260 Qunderline
= intern ("underline");
1261 staticpro (&Qread_only
);
1262 Qread_only
= intern ("read-only");
1263 staticpro (&Qinvisible
);
1264 Qinvisible
= intern ("invisible");
1265 staticpro (&Qhidden
);
1266 Qhidden
= intern ("hidden");
1267 staticpro (&Qcategory
);
1268 Qcategory
= intern ("category");
1269 staticpro (&Qlocal_map
);
1270 Qlocal_map
= intern ("local-map");
1271 staticpro (&Qfront_sticky
);
1272 Qfront_sticky
= intern ("front-sticky");
1273 staticpro (&Qrear_nonsticky
);
1274 Qrear_nonsticky
= intern ("rear-nonsticky");
1276 /* Properties that text might use to specify certain actions */
1278 staticpro (&Qmouse_left
);
1279 Qmouse_left
= intern ("mouse-left");
1280 staticpro (&Qmouse_entered
);
1281 Qmouse_entered
= intern ("mouse-entered");
1282 staticpro (&Qpoint_left
);
1283 Qpoint_left
= intern ("point-left");
1284 staticpro (&Qpoint_entered
);
1285 Qpoint_entered
= intern ("point-entered");
1287 defsubr (&Stext_properties_at
);
1288 defsubr (&Sget_text_property
);
1289 defsubr (&Snext_property_change
);
1290 defsubr (&Snext_single_property_change
);
1291 defsubr (&Sprevious_property_change
);
1292 defsubr (&Sprevious_single_property_change
);
1293 defsubr (&Sadd_text_properties
);
1294 defsubr (&Sput_text_property
);
1295 defsubr (&Sset_text_properties
);
1296 defsubr (&Sremove_text_properties
);
1297 defsubr (&Stext_property_any
);
1298 defsubr (&Stext_property_not_all
);
1299 /* defsubr (&Serase_text_properties); */
1300 /* defsubr (&Scopy_text_properties); */
1305 lose
-- this shouldn
't be compiled if USE_TEXT_PROPERTIES isn't defined
1307 #endif /* USE_TEXT_PROPERTIES */