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 1, 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 neccessary 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 Qmodification_hooks
;
47 Lisp_Object Qcategory
;
48 Lisp_Object Qlocal_map
;
50 /* Visual properties text (including strings) may have. */
51 Lisp_Object Qforeground
, Qbackground
, Qfont
, Qunderline
, Qstipple
;
52 Lisp_Object Qinvisible
, Qread_only
;
54 /* Extract the interval at the position pointed to by BEGIN from
55 OBJECT, a string or buffer. Additionally, check that the positions
56 pointed to by BEGIN and END are within the bounds of OBJECT, and
57 reverse them if *BEGIN is greater than *END. The objects pointed
58 to by BEGIN and END may be integers or markers; if the latter, they
59 are coerced to integers.
61 When OBJECT is a string, we increment *BEGIN and *END
62 to make them origin-one.
64 Note that buffer points don't correspond to interval indices.
65 For example, point-max is 1 greater than the index of the last
66 character. This difference is handled in the caller, which uses
67 the validated points to determine a length, and operates on that.
68 Exceptions are Ftext_properties_at, Fnext_property_change, and
69 Fprevious_property_change which call this function with BEGIN == END.
70 Handle this case specially.
72 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
73 create an interval tree for OBJECT if one doesn't exist, provided
74 the object actually contains text. In the current design, if there
75 is no text, there can be no text properties. */
81 validate_interval_range (object
, begin
, end
, force
)
82 Lisp_Object object
, *begin
, *end
;
88 CHECK_STRING_OR_BUFFER (object
, 0);
89 CHECK_NUMBER_COERCE_MARKER (*begin
, 0);
90 CHECK_NUMBER_COERCE_MARKER (*end
, 0);
92 /* If we are asked for a point, but from a subr which operates
93 on a range, then return nothing. */
94 if (*begin
== *end
&& begin
!= end
)
97 if (XINT (*begin
) > XINT (*end
))
105 if (XTYPE (object
) == Lisp_Buffer
)
107 register struct buffer
*b
= XBUFFER (object
);
109 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
110 && XINT (*end
) <= BUF_ZV (b
)))
111 args_out_of_range (*begin
, *end
);
114 /* If there's no text, there are no properties. */
115 if (BUF_BEGV (b
) == BUF_ZV (b
))
116 return NULL_INTERVAL
;
118 searchpos
= XINT (*begin
);
119 if (searchpos
== BUF_Z (b
))
122 /* Special case for point-max: return the interval for the
124 if (*begin
== *end
&& *begin
== BUF_Z (b
))
130 register struct Lisp_String
*s
= XSTRING (object
);
132 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
133 && XINT (*end
) <= s
->size
))
134 args_out_of_range (*begin
, *end
);
135 /* User-level Positions in strings start with 0,
136 but the interval code always wants positions starting with 1. */
137 XFASTINT (*begin
) += 1;
138 XFASTINT (*end
) += 1;
142 return NULL_INTERVAL
;
144 searchpos
= XINT (*begin
);
145 if (searchpos
> s
->size
)
149 if (NULL_INTERVAL_P (i
))
150 return (force
? create_root_interval (object
) : i
);
152 return find_interval (i
, searchpos
);
155 /* Validate LIST as a property list. If LIST is not a list, then
156 make one consisting of (LIST nil). Otherwise, verify that LIST
157 is even numbered and thus suitable as a plist. */
160 validate_plist (list
)
168 register Lisp_Object tail
;
169 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
172 error ("Odd length text property list");
176 return Fcons (list
, Fcons (Qnil
, Qnil
));
179 /* Return nonzero if interval I has all the properties,
180 with the same values, of list PLIST. */
183 interval_has_all_properties (plist
, i
)
187 register Lisp_Object tail1
, tail2
, sym1
, sym2
;
190 /* Go through each element of PLIST. */
191 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
196 /* Go through I's plist, looking for sym1 */
197 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
198 if (EQ (sym1
, Fcar (tail2
)))
200 /* Found the same property on both lists. If the
201 values are unequal, return zero. */
202 if (! EQ (Fequal (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))),
206 /* Property has same value on both lists; go to next one. */
218 /* Return nonzero if the plist of interval I has any of the
219 properties of PLIST, regardless of their values. */
222 interval_has_some_properties (plist
, i
)
226 register Lisp_Object tail1
, tail2
, sym
;
228 /* Go through each element of PLIST. */
229 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
233 /* Go through i's plist, looking for tail1 */
234 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
235 if (EQ (sym
, Fcar (tail2
)))
242 /* Set the properties of INTERVAL to PROPERTIES,
243 and record undo info for the previous values.
244 OBJECT is the string or buffer that INTERVAL belongs to. */
247 set_properties (properties
, interval
, object
)
248 Lisp_Object properties
, object
;
251 Lisp_Object oldprops
;
252 oldprops
= interval
->plist
;
254 /* Record undo for old properties. */
255 while (XTYPE (oldprops
) == Lisp_Cons
)
258 sym
= Fcar (oldprops
);
259 record_property_change (interval
->position
, LENGTH (interval
),
260 sym
, Fcar_safe (Fcdr (oldprops
)),
263 oldprops
= Fcdr_safe (Fcdr (oldprops
));
266 /* Store new properties. */
267 interval
->plist
= Fcopy_sequence (properties
);
270 /* Add the properties of PLIST to the interval I, or set
271 the value of I's property to the value of the property on PLIST
272 if they are different.
274 OBJECT should be the string or buffer the interval is in.
276 Return nonzero if this changes I (i.e., if any members of PLIST
277 are actually added to I's plist) */
280 add_properties (plist
, i
, object
)
285 register Lisp_Object tail1
, tail2
, sym1
, val1
;
286 register int changed
= 0;
289 /* Go through each element of PLIST. */
290 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
293 val1
= Fcar (Fcdr (tail1
));
296 /* Go through I's plist, looking for sym1 */
297 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
298 if (EQ (sym1
, Fcar (tail2
)))
300 register Lisp_Object this_cdr
= Fcdr (tail2
);
302 /* Found the property. Now check its value. */
305 /* The properties have the same value on both lists.
306 Continue to the next property. */
307 if (!NILP (Fequal (val1
, Fcar (this_cdr
))))
310 /* Record this change in the buffer, for undo purposes. */
311 if (XTYPE (object
) == Lisp_Buffer
)
313 record_property_change (i
->position
, LENGTH (i
),
314 sym1
, Fcar (this_cdr
), object
);
315 modify_region (XBUFFER (object
),
316 make_number (i
->position
),
317 make_number (i
->position
+ LENGTH (i
)));
320 /* I's property has a different value -- change it */
321 Fsetcar (this_cdr
, val1
);
328 /* Record this change in the buffer, for undo purposes. */
329 if (XTYPE (object
) == Lisp_Buffer
)
331 record_property_change (i
->position
, LENGTH (i
),
333 modify_region (XBUFFER (object
),
334 make_number (i
->position
),
335 make_number (i
->position
+ LENGTH (i
)));
337 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
345 /* For any members of PLIST which are properties of I, remove them
347 OBJECT is the string or buffer containing I. */
350 remove_properties (plist
, i
, object
)
355 register Lisp_Object tail1
, tail2
, sym
;
356 register Lisp_Object current_plist
= i
->plist
;
357 register int changed
= 0;
359 /* Go through each element of plist. */
360 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
364 /* First, remove the symbol if its at the head of the list */
365 while (! NILP (current_plist
) && EQ (sym
, Fcar (current_plist
)))
367 if (XTYPE (object
) == Lisp_Buffer
)
369 record_property_change (i
->position
, LENGTH (i
),
370 sym
, Fcar (Fcdr (current_plist
)),
372 modify_region (XBUFFER (object
),
373 make_number (i
->position
),
374 make_number (i
->position
+ LENGTH (i
)));
377 current_plist
= Fcdr (Fcdr (current_plist
));
381 /* Go through i's plist, looking for sym */
382 tail2
= current_plist
;
383 while (! NILP (tail2
))
385 register Lisp_Object
this = Fcdr (Fcdr (tail2
));
386 if (EQ (sym
, Fcar (this)))
388 if (XTYPE (object
) == Lisp_Buffer
)
390 record_property_change (i
->position
, LENGTH (i
),
391 sym
, Fcar (Fcdr (this)), object
);
392 modify_region (XBUFFER (object
),
393 make_number (i
->position
),
394 make_number (i
->position
+ LENGTH (i
)));
397 Fsetcdr (Fcdr (tail2
), Fcdr (Fcdr (this)));
405 i
->plist
= current_plist
;
410 /* Remove all properties from interval I. Return non-zero
411 if this changes the interval. */
425 DEFUN ("text-properties-at", Ftext_properties_at
,
426 Stext_properties_at
, 1, 2, 0,
427 "Return the list of properties held by the character at POSITION\n\
428 in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
429 defaults to the current buffer.\n\
430 If POSITION is at the end of OBJECT, the value is nil.")
432 Lisp_Object pos
, object
;
437 XSET (object
, Lisp_Buffer
, current_buffer
);
439 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
440 if (NULL_INTERVAL_P (i
))
442 /* If POS is at the end of the interval,
443 it means it's the end of OBJECT.
444 There are no properties at the very end,
445 since no character follows. */
446 if (XINT (pos
) == LENGTH (i
) + i
->position
)
452 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
453 "Return the value of position POS's property PROP, in OBJECT.\n\
454 OBJECT is optional and defaults to the current buffer.\n\
455 If POSITION is at the end of OBJECT, the value is nil.")
457 Lisp_Object pos
, object
;
458 register Lisp_Object prop
;
461 register Lisp_Object tail
;
464 XSET (object
, Lisp_Buffer
, current_buffer
);
465 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
466 if (NULL_INTERVAL_P (i
))
469 /* If POS is at the end of the interval,
470 it means it's the end of OBJECT.
471 There are no properties at the very end,
472 since no character follows. */
473 if (XINT (pos
) == LENGTH (i
) + i
->position
)
476 return textget (i
->plist
, prop
);
479 DEFUN ("next-property-change", Fnext_property_change
,
480 Snext_property_change
, 1, 2, 0,
481 "Return the position of next property change.\n\
482 Scans characters forward from POS in OBJECT till it finds\n\
483 a change in some text property, then returns the position of the change.\n\
484 The optional second argument OBJECT is the string or buffer to scan.\n\
485 Return nil if the property is constant all the way to the end of OBJECT.\n\
486 If the value is non-nil, it is a position greater than POS, never equal.")
488 Lisp_Object pos
, object
;
490 register INTERVAL i
, next
;
493 XSET (object
, Lisp_Buffer
, current_buffer
);
495 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
496 if (NULL_INTERVAL_P (i
))
499 next
= next_interval (i
);
500 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
501 next
= next_interval (next
);
503 if (NULL_INTERVAL_P (next
))
506 return next
->position
- (XTYPE (object
) == Lisp_String
);
510 DEFUN ("next-single-property-change", Fnext_single_property_change
,
511 Snext_single_property_change
, 1, 3, 0,
512 "Return the position of next property change for a specific property.\n\
513 Scans characters forward from POS till it finds\n\
514 a change in the PROP property, then returns the position of the change.\n\
515 The optional third argument OBJECT is the string or buffer to scan.\n\
516 Return nil if the property is constant all the way to the end of OBJECT.\n\
517 If the value is non-nil, it is a position greater than POS, never equal.")
519 Lisp_Object pos
, prop
, object
;
521 register INTERVAL i
, next
;
522 register Lisp_Object here_val
;
525 XSET (object
, Lisp_Buffer
, current_buffer
);
527 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
528 if (NULL_INTERVAL_P (i
))
531 here_val
= textget (i
->plist
, prop
);
532 next
= next_interval (i
);
533 while (! NULL_INTERVAL_P (next
)
534 && EQ (here_val
, textget (next
->plist
, prop
)))
535 next
= next_interval (next
);
537 if (NULL_INTERVAL_P (next
))
540 return next
->position
- (XTYPE (object
) == Lisp_String
);
543 DEFUN ("previous-property-change", Fprevious_property_change
,
544 Sprevious_property_change
, 1, 2, 0,
545 "Return the position of previous property change.\n\
546 Scans characters backwards from POS in OBJECT till it finds\n\
547 a change in some text property, then returns the position of the change.\n\
548 The optional second argument OBJECT is the string or buffer to scan.\n\
549 Return nil if the property is constant all the way to the start of OBJECT.\n\
550 If the value is non-nil, it is a position less than POS, never equal.")
552 Lisp_Object pos
, object
;
554 register INTERVAL i
, previous
;
557 XSET (object
, Lisp_Buffer
, current_buffer
);
559 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
560 if (NULL_INTERVAL_P (i
))
563 previous
= previous_interval (i
);
564 while (! NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
))
565 previous
= previous_interval (previous
);
566 if (NULL_INTERVAL_P (previous
))
569 return (previous
->position
+ LENGTH (previous
) - 1
570 - (XTYPE (object
) == Lisp_String
));
573 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
574 Sprevious_single_property_change
, 2, 3, 0,
575 "Return the position of previous property change for a specific property.\n\
576 Scans characters backward from POS till it finds\n\
577 a change in the PROP property, then returns the position of the change.\n\
578 The optional third argument OBJECT is the string or buffer to scan.\n\
579 Return nil if the property is constant all the way to the start of OBJECT.\n\
580 If the value is non-nil, it is a position less than POS, never equal.")
582 Lisp_Object pos
, prop
, object
;
584 register INTERVAL i
, previous
;
585 register Lisp_Object here_val
;
588 XSET (object
, Lisp_Buffer
, current_buffer
);
590 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
591 if (NULL_INTERVAL_P (i
))
594 here_val
= textget (i
->plist
, prop
);
595 previous
= previous_interval (i
);
596 while (! NULL_INTERVAL_P (previous
)
597 && EQ (here_val
, textget (previous
->plist
, prop
)))
598 previous
= previous_interval (previous
);
599 if (NULL_INTERVAL_P (previous
))
602 return (previous
->position
+ LENGTH (previous
) - 1
603 - (XTYPE (object
) == Lisp_String
));
606 DEFUN ("add-text-properties", Fadd_text_properties
,
607 Sadd_text_properties
, 3, 4, 0,
608 "Add properties to the text from START to END.\n\
609 The third argument PROPS is a property list\n\
610 specifying the property values to add.\n\
611 The optional fourth argument, OBJECT,\n\
612 is the string or buffer containing the text.\n\
613 Return t if any property value actually changed, nil otherwise.")
614 (start
, end
, properties
, object
)
615 Lisp_Object start
, end
, properties
, object
;
617 register INTERVAL i
, unchanged
;
618 register int s
, len
, modified
= 0;
620 properties
= validate_plist (properties
);
621 if (NILP (properties
))
625 XSET (object
, Lisp_Buffer
, current_buffer
);
627 i
= validate_interval_range (object
, &start
, &end
, hard
);
628 if (NULL_INTERVAL_P (i
))
632 len
= XINT (end
) - s
;
634 /* If we're not starting on an interval boundary, we have to
635 split this interval. */
636 if (i
->position
!= s
)
638 /* If this interval already has the properties, we can
640 if (interval_has_all_properties (properties
, i
))
642 int got
= (LENGTH (i
) - (s
- i
->position
));
650 i
= split_interval_right (unchanged
, s
- unchanged
->position
+ 1);
651 copy_properties (unchanged
, i
);
655 /* We are at the beginning of interval I, with LEN chars to scan. */
661 if (LENGTH (i
) >= len
)
663 if (interval_has_all_properties (properties
, i
))
664 return modified
? Qt
: Qnil
;
666 if (LENGTH (i
) == len
)
668 add_properties (properties
, i
, object
);
672 /* i doesn't have the properties, and goes past the change limit */
674 i
= split_interval_left (unchanged
, len
+ 1);
675 copy_properties (unchanged
, i
);
676 add_properties (properties
, i
, object
);
681 modified
+= add_properties (properties
, i
, object
);
682 i
= next_interval (i
);
686 DEFUN ("put-text-property", Fput_text_property
,
687 Sput_text_property
, 4, 5, 0,
688 "Set one property of the text from START to END.\n\
689 The third and fourth arguments PROP and VALUE\n\
690 specify the property to add.\n\
691 The optional fifth argument, OBJECT,\n\
692 is the string or buffer containing the text.")
693 (start
, end
, prop
, value
, object
)
694 Lisp_Object start
, end
, prop
, value
, object
;
696 Fadd_text_properties (start
, end
,
697 Fcons (prop
, Fcons (value
, Qnil
)),
702 DEFUN ("set-text-properties", Fset_text_properties
,
703 Sset_text_properties
, 3, 4, 0,
704 "Completely replace properties of text from START to END.\n\
705 The third argument PROPS is the new property list.\n\
706 The optional fourth argument, OBJECT,\n\
707 is the string or buffer containing the text.")
708 (start
, end
, props
, object
)
709 Lisp_Object start
, end
, props
, object
;
711 register INTERVAL i
, unchanged
;
712 register INTERVAL prev_changed
= NULL_INTERVAL
;
715 props
= validate_plist (props
);
720 XSET (object
, Lisp_Buffer
, current_buffer
);
722 i
= validate_interval_range (object
, &start
, &end
, hard
);
723 if (NULL_INTERVAL_P (i
))
727 len
= XINT (end
) - s
;
729 if (i
->position
!= s
)
732 i
= split_interval_right (unchanged
, s
- unchanged
->position
+ 1);
734 if (LENGTH (i
) > len
)
736 copy_properties (unchanged
, i
);
737 i
= split_interval_left (i
, len
+ 1);
738 set_properties (props
, i
, object
);
742 set_properties (props
, i
, object
);
744 if (LENGTH (i
) == len
)
749 i
= next_interval (i
);
752 /* We are starting at the beginning of an interval, I */
758 if (LENGTH (i
) >= len
)
760 if (LENGTH (i
) > len
)
761 i
= split_interval_left (i
, len
+ 1);
763 if (NULL_INTERVAL_P (prev_changed
))
764 set_properties (props
, i
, object
);
766 merge_interval_left (i
);
771 if (NULL_INTERVAL_P (prev_changed
))
773 set_properties (props
, i
, object
);
777 prev_changed
= i
= merge_interval_left (i
);
779 i
= next_interval (i
);
785 DEFUN ("remove-text-properties", Fremove_text_properties
,
786 Sremove_text_properties
, 3, 4, 0,
787 "Remove some properties from text from START to END.\n\
788 The third argument PROPS is a property list\n\
789 whose property names specify the properties to remove.\n\
790 \(The values stored in PROPS are ignored.)\n\
791 The optional fourth argument, OBJECT,\n\
792 is the string or buffer containing the text.\n\
793 Return t if any property was actually removed, nil otherwise.")
794 (start
, end
, props
, object
)
795 Lisp_Object start
, end
, props
, object
;
797 register INTERVAL i
, unchanged
;
798 register int s
, len
, modified
= 0;
801 XSET (object
, Lisp_Buffer
, current_buffer
);
803 i
= validate_interval_range (object
, &start
, &end
, soft
);
804 if (NULL_INTERVAL_P (i
))
808 len
= XINT (end
) - s
;
810 if (i
->position
!= s
)
812 /* No properties on this first interval -- return if
813 it covers the entire region. */
814 if (! interval_has_some_properties (props
, i
))
816 int got
= (LENGTH (i
) - (s
- i
->position
));
821 /* Split away the beginning of this interval; what we don't
826 i
= split_interval_right (unchanged
, s
- unchanged
->position
+ 1);
827 copy_properties (unchanged
, i
);
831 /* We are at the beginning of an interval, with len to scan */
837 if (LENGTH (i
) >= len
)
839 if (! interval_has_some_properties (props
, i
))
840 return modified
? Qt
: Qnil
;
842 if (LENGTH (i
) == len
)
844 remove_properties (props
, i
, object
);
848 /* i has the properties, and goes past the change limit */
850 i
= split_interval_left (i
, len
+ 1);
851 copy_properties (unchanged
, i
);
852 remove_properties (props
, i
, object
);
857 modified
+= remove_properties (props
, i
, object
);
858 i
= next_interval (i
);
862 #if 0 /* You can use set-text-properties for this. */
864 DEFUN ("erase-text-properties", Ferase_text_properties
,
865 Serase_text_properties
, 2, 3, 0,
866 "Remove all properties from the text from START to END.\n\
867 The optional third argument, OBJECT,\n\
868 is the string or buffer containing the text.")
870 Lisp_Object start
, end
, object
;
873 register INTERVAL prev_changed
= NULL_INTERVAL
;
874 register int s
, len
, modified
;
877 XSET (object
, Lisp_Buffer
, current_buffer
);
879 i
= validate_interval_range (object
, &start
, &end
, soft
);
880 if (NULL_INTERVAL_P (i
))
884 len
= XINT (end
) - s
;
886 if (i
->position
!= s
)
889 register INTERVAL unchanged
= i
;
891 /* If there are properties here, then this text will be modified. */
892 if (! NILP (i
->plist
))
894 i
= split_interval_right (unchanged
, s
- unchanged
->position
+ 1);
898 if (LENGTH (i
) > len
)
900 i
= split_interval_right (i
, len
+ 1);
901 copy_properties (unchanged
, i
);
905 if (LENGTH (i
) == len
)
910 /* If the text of I is without any properties, and contains
911 LEN or more characters, then we may return without changing
913 else if (LENGTH (i
) - (s
- i
->position
) <= len
)
915 /* The amount of text to change extends past I, so just note
916 how much we've gotten. */
918 got
= LENGTH (i
) - (s
- i
->position
);
922 i
= next_interval (i
);
925 /* We are starting at the beginning of an interval, I. */
928 if (LENGTH (i
) >= len
)
930 /* If I has no properties, simply merge it if possible. */
933 if (! NULL_INTERVAL_P (prev_changed
))
934 merge_interval_left (i
);
936 return modified
? Qt
: Qnil
;
939 if (LENGTH (i
) > len
)
940 i
= split_interval_left (i
, len
+ 1);
941 if (! NULL_INTERVAL_P (prev_changed
))
942 merge_interval_left (i
);
949 /* Here if we still need to erase past the end of I */
951 if (NULL_INTERVAL_P (prev_changed
))
953 modified
+= erase_properties (i
);
958 modified
+= ! NILP (i
->plist
);
959 /* Merging I will give it the properties of PREV_CHANGED. */
960 prev_changed
= i
= merge_interval_left (i
);
963 i
= next_interval (i
);
966 return modified
? Qt
: Qnil
;
973 DEFVAR_INT ("interval-balance-threshold", &interval_balance_threshold
,
974 "Threshold for rebalancing interval trees, expressed as the\n\
975 percentage by which the left interval tree should not differ from the right.");
976 interval_balance_threshold
= 8;
978 /* Common attributes one might give text */
980 staticpro (&Qforeground
);
981 Qforeground
= intern ("foreground");
982 staticpro (&Qbackground
);
983 Qbackground
= intern ("background");
985 Qfont
= intern ("font");
986 staticpro (&Qstipple
);
987 Qstipple
= intern ("stipple");
988 staticpro (&Qunderline
);
989 Qunderline
= intern ("underline");
990 staticpro (&Qread_only
);
991 Qread_only
= intern ("read-only");
992 staticpro (&Qinvisible
);
993 Qinvisible
= intern ("invisible");
994 staticpro (&Qcategory
);
995 Qcategory
= intern ("category");
996 staticpro (&Qlocal_map
);
997 Qlocal_map
= intern ("local-map");
999 /* Properties that text might use to specify certain actions */
1001 staticpro (&Qmouse_left
);
1002 Qmouse_left
= intern ("mouse-left");
1003 staticpro (&Qmouse_entered
);
1004 Qmouse_entered
= intern ("mouse-entered");
1005 staticpro (&Qpoint_left
);
1006 Qpoint_left
= intern ("point-left");
1007 staticpro (&Qpoint_entered
);
1008 Qpoint_entered
= intern ("point-entered");
1009 staticpro (&Qmodification_hooks
);
1010 Qmodification_hooks
= intern ("modification-hooks");
1012 defsubr (&Stext_properties_at
);
1013 defsubr (&Sget_text_property
);
1014 defsubr (&Snext_property_change
);
1015 defsubr (&Snext_single_property_change
);
1016 defsubr (&Sprevious_property_change
);
1017 defsubr (&Sprevious_single_property_change
);
1018 defsubr (&Sadd_text_properties
);
1019 defsubr (&Sput_text_property
);
1020 defsubr (&Sset_text_properties
);
1021 defsubr (&Sremove_text_properties
);
1022 /* defsubr (&Serase_text_properties); */
1027 lose
-- this shouldn
't be compiled if USE_TEXT_PROPERTIES isn't defined
1029 #endif /* USE_TEXT_PROPERTIES */