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"
27 /* NOTES: previous- and next- property change will have to skip
28 zero-length intervals if they are implemented. This could be done
29 inside next_interval and previous_interval.
31 set_properties needs to deal with the interval property cache.
33 It is assumed that for any interval plist, a property appears
34 only once on the list. Although some code i.e., remove_properties,
35 handles the more general case, the uniqueness of properties is
36 necessary for the system to remain consistent. This requirement
37 is enforced by the subrs installing properties onto the intervals. */
39 /* The rest of the file is within this conditional */
40 #ifdef USE_TEXT_PROPERTIES
43 Lisp_Object Qmouse_left
;
44 Lisp_Object Qmouse_entered
;
45 Lisp_Object Qpoint_left
;
46 Lisp_Object Qpoint_entered
;
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
, Qintangible
;
54 /* Sticky properties */
55 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
57 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
58 the o1's cdr. Otherwise, return zero. This is handy for
60 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && CONSP ((o2) = XCONS (o1)->cdr))
62 Lisp_Object Vinhibit_point_motion_hooks
;
65 /* Extract the interval at the position pointed to by BEGIN from
66 OBJECT, a string or buffer. Additionally, check that the positions
67 pointed to by BEGIN and END are within the bounds of OBJECT, and
68 reverse them if *BEGIN is greater than *END. The objects pointed
69 to by BEGIN and END may be integers or markers; if the latter, they
70 are coerced to integers.
72 When OBJECT is a string, we increment *BEGIN and *END
73 to make them origin-one.
75 Note that buffer points don't correspond to interval indices.
76 For example, point-max is 1 greater than the index of the last
77 character. This difference is handled in the caller, which uses
78 the validated points to determine a length, and operates on that.
79 Exceptions are Ftext_properties_at, Fnext_property_change, and
80 Fprevious_property_change which call this function with BEGIN == END.
81 Handle this case specially.
83 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
84 create an interval tree for OBJECT if one doesn't exist, provided
85 the object actually contains text. In the current design, if there
86 is no text, there can be no text properties. */
92 validate_interval_range (object
, begin
, end
, force
)
93 Lisp_Object object
, *begin
, *end
;
99 CHECK_STRING_OR_BUFFER (object
, 0);
100 CHECK_NUMBER_COERCE_MARKER (*begin
, 0);
101 CHECK_NUMBER_COERCE_MARKER (*end
, 0);
103 /* If we are asked for a point, but from a subr which operates
104 on a range, then return nothing. */
105 if (*begin
== *end
&& begin
!= end
)
106 return NULL_INTERVAL
;
108 if (XINT (*begin
) > XINT (*end
))
116 if (XTYPE (object
) == Lisp_Buffer
)
118 register struct buffer
*b
= XBUFFER (object
);
120 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
121 && XINT (*end
) <= BUF_ZV (b
)))
122 args_out_of_range (*begin
, *end
);
125 /* If there's no text, there are no properties. */
126 if (BUF_BEGV (b
) == BUF_ZV (b
))
127 return NULL_INTERVAL
;
129 searchpos
= XINT (*begin
);
133 register struct Lisp_String
*s
= XSTRING (object
);
135 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
136 && XINT (*end
) <= s
->size
))
137 args_out_of_range (*begin
, *end
);
138 /* User-level Positions in strings start with 0,
139 but the interval code always wants positions starting with 1. */
140 XFASTINT (*begin
) += 1;
142 XFASTINT (*end
) += 1;
146 return NULL_INTERVAL
;
148 searchpos
= XINT (*begin
);
151 if (NULL_INTERVAL_P (i
))
152 return (force
? create_root_interval (object
) : i
);
154 return find_interval (i
, searchpos
);
157 /* Validate LIST as a property list. If LIST is not a list, then
158 make one consisting of (LIST nil). Otherwise, verify that LIST
159 is even numbered and thus suitable as a plist. */
162 validate_plist (list
)
171 register Lisp_Object tail
;
172 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
178 error ("Odd length text property list");
182 return Fcons (list
, Fcons (Qnil
, Qnil
));
185 /* Return nonzero if interval I has all the properties,
186 with the same values, of list PLIST. */
189 interval_has_all_properties (plist
, i
)
193 register Lisp_Object tail1
, tail2
, sym1
, sym2
;
196 /* Go through each element of PLIST. */
197 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
202 /* Go through I's plist, looking for sym1 */
203 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
204 if (EQ (sym1
, Fcar (tail2
)))
206 /* Found the same property on both lists. If the
207 values are unequal, return zero. */
208 if (! EQ (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))))
211 /* Property has same value on both lists; go to next one. */
223 /* Return nonzero if the plist of interval I has any of the
224 properties of PLIST, regardless of their values. */
227 interval_has_some_properties (plist
, i
)
231 register Lisp_Object tail1
, tail2
, sym
;
233 /* Go through each element of PLIST. */
234 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
238 /* Go through i's plist, looking for tail1 */
239 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
240 if (EQ (sym
, Fcar (tail2
)))
247 /* Changing the plists of individual intervals. */
249 /* Return the value of PROP in property-list PLIST, or Qunbound if it
252 property_value (plist
, prop
)
256 while (PLIST_ELT_P (plist
, value
))
257 if (EQ (XCONS (plist
)->car
, prop
))
258 return XCONS (value
)->car
;
260 plist
= XCONS (value
)->cdr
;
265 /* Set the properties of INTERVAL to PROPERTIES,
266 and record undo info for the previous values.
267 OBJECT is the string or buffer that INTERVAL belongs to. */
270 set_properties (properties
, interval
, object
)
271 Lisp_Object properties
, object
;
274 Lisp_Object sym
, value
;
276 if (BUFFERP (object
))
278 /* For each property in the old plist which is missing from PROPERTIES,
279 or has a different value in PROPERTIES, make an undo record. */
280 for (sym
= interval
->plist
;
281 PLIST_ELT_P (sym
, value
);
282 sym
= XCONS (value
)->cdr
)
283 if (! EQ (property_value (properties
, XCONS (sym
)->car
),
286 modify_region (XBUFFER (object
),
287 make_number (interval
->position
),
288 make_number (interval
->position
+ LENGTH (interval
)));
289 record_property_change (interval
->position
, LENGTH (interval
),
290 XCONS (sym
)->car
, XCONS (value
)->car
,
294 /* For each new property that has no value at all in the old plist,
295 make an undo record binding it to nil, so it will be removed. */
296 for (sym
= properties
;
297 PLIST_ELT_P (sym
, value
);
298 sym
= XCONS (value
)->cdr
)
299 if (EQ (property_value (interval
->plist
, XCONS (sym
)->car
), Qunbound
))
301 modify_region (XBUFFER (object
),
302 make_number (interval
->position
),
303 make_number (interval
->position
+ LENGTH (interval
)));
304 record_property_change (interval
->position
, LENGTH (interval
),
305 XCONS (sym
)->car
, Qnil
,
310 /* Store new properties. */
311 interval
->plist
= Fcopy_sequence (properties
);
314 /* Add the properties of PLIST to the interval I, or set
315 the value of I's property to the value of the property on PLIST
316 if they are different.
318 OBJECT should be the string or buffer the interval is in.
320 Return nonzero if this changes I (i.e., if any members of PLIST
321 are actually added to I's plist) */
324 add_properties (plist
, i
, object
)
329 register Lisp_Object tail1
, tail2
, sym1
, val1
;
330 register int changed
= 0;
333 /* Go through each element of PLIST. */
334 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
337 val1
= Fcar (Fcdr (tail1
));
340 /* Go through I's plist, looking for sym1 */
341 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
342 if (EQ (sym1
, Fcar (tail2
)))
344 register Lisp_Object this_cdr
;
346 this_cdr
= Fcdr (tail2
);
347 /* Found the property. Now check its value. */
350 /* The properties have the same value on both lists.
351 Continue to the next property. */
352 if (EQ (val1
, Fcar (this_cdr
)))
355 /* Record this change in the buffer, for undo purposes. */
356 if (XTYPE (object
) == Lisp_Buffer
)
358 modify_region (XBUFFER (object
),
359 make_number (i
->position
),
360 make_number (i
->position
+ LENGTH (i
)));
361 record_property_change (i
->position
, LENGTH (i
),
362 sym1
, Fcar (this_cdr
), object
);
365 /* I's property has a different value -- change it */
366 Fsetcar (this_cdr
, val1
);
373 /* Record this change in the buffer, for undo purposes. */
374 if (XTYPE (object
) == Lisp_Buffer
)
376 modify_region (XBUFFER (object
),
377 make_number (i
->position
),
378 make_number (i
->position
+ LENGTH (i
)));
379 record_property_change (i
->position
, LENGTH (i
),
382 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
390 /* For any members of PLIST which are properties of I, remove them
392 OBJECT is the string or buffer containing I. */
395 remove_properties (plist
, i
, object
)
400 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
401 register int changed
= 0;
403 current_plist
= i
->plist
;
404 /* Go through each element of plist. */
405 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
409 /* First, remove the symbol if its at the head of the list */
410 while (! NILP (current_plist
) && EQ (sym
, Fcar (current_plist
)))
412 if (XTYPE (object
) == Lisp_Buffer
)
414 modify_region (XBUFFER (object
),
415 make_number (i
->position
),
416 make_number (i
->position
+ LENGTH (i
)));
417 record_property_change (i
->position
, LENGTH (i
),
418 sym
, Fcar (Fcdr (current_plist
)),
422 current_plist
= Fcdr (Fcdr (current_plist
));
426 /* Go through i's plist, looking for sym */
427 tail2
= current_plist
;
428 while (! NILP (tail2
))
430 register Lisp_Object
this;
431 this = Fcdr (Fcdr (tail2
));
432 if (EQ (sym
, Fcar (this)))
434 if (XTYPE (object
) == Lisp_Buffer
)
436 modify_region (XBUFFER (object
),
437 make_number (i
->position
),
438 make_number (i
->position
+ LENGTH (i
)));
439 record_property_change (i
->position
, LENGTH (i
),
440 sym
, Fcar (Fcdr (this)), object
);
443 Fsetcdr (Fcdr (tail2
), Fcdr (Fcdr (this)));
451 i
->plist
= current_plist
;
456 /* Remove all properties from interval I. Return non-zero
457 if this changes the interval. */
471 DEFUN ("text-properties-at", Ftext_properties_at
,
472 Stext_properties_at
, 1, 2, 0,
473 "Return the list of properties held by the character at POSITION\n\
474 in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
475 defaults to the current buffer.\n\
476 If POSITION is at the end of OBJECT, the value is nil.")
478 Lisp_Object pos
, object
;
483 XSET (object
, Lisp_Buffer
, current_buffer
);
485 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
486 if (NULL_INTERVAL_P (i
))
488 /* If POS is at the end of the interval,
489 it means it's the end of OBJECT.
490 There are no properties at the very end,
491 since no character follows. */
492 if (XINT (pos
) == LENGTH (i
) + i
->position
)
498 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
499 "Return the value of position POS's property PROP, in OBJECT.\n\
500 OBJECT is optional and defaults to the current buffer.\n\
501 If POSITION is at the end of OBJECT, the value is nil.")
503 Lisp_Object pos
, object
;
504 register Lisp_Object prop
;
507 register Lisp_Object tail
;
510 XSET (object
, Lisp_Buffer
, current_buffer
);
511 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
512 if (NULL_INTERVAL_P (i
))
515 /* If POS is at the end of the interval,
516 it means it's the end of OBJECT.
517 There are no properties at the very end,
518 since no character follows. */
519 if (XINT (pos
) == LENGTH (i
) + i
->position
)
522 return textget (i
->plist
, prop
);
525 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
526 "Return the value of position POS's property PROP, in OBJECT.\n\
527 OBJECT is optional and defaults to the current buffer.\n\
528 If POS is at the end of OBJECT, the value is nil.\n\
529 If OBJECT is a buffer, then overlay properties are considered as well as\n\
531 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
532 overlays are considered only if they are associated with OBJECT.")
534 Lisp_Object pos
, object
;
535 register Lisp_Object prop
;
537 struct window
*w
= 0;
539 CHECK_NUMBER_COERCE_MARKER (pos
, 0);
542 XSET (object
, Lisp_Buffer
, current_buffer
);
544 if (WINDOWP (object
))
546 w
= XWINDOW (object
);
547 XSET (object
, Lisp_Buffer
, w
->buffer
);
549 if (BUFFERP (object
))
551 int posn
= XINT (pos
);
553 Lisp_Object
*overlay_vec
, tem
;
557 /* First try with room for 40 overlays. */
559 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
561 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
, &next_overlay
);
563 /* If there are more than 40,
564 make enough space for all, and try again. */
568 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
569 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
, &next_overlay
);
571 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
573 /* Now check the overlays in order of decreasing priority. */
574 while (--noverlays
>= 0)
576 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
581 /* Not a buffer, or no appropriate overlay, so fall through to the
583 return (Fget_text_property (pos
, prop
, object
));
586 DEFUN ("next-property-change", Fnext_property_change
,
587 Snext_property_change
, 1, 3, 0,
588 "Return the position of next property change.\n\
589 Scans characters forward from POS in OBJECT till it finds\n\
590 a change in some text property, then returns the position of the change.\n\
591 The optional second argument OBJECT is the string or buffer to scan.\n\
592 Return nil if the property is constant all the way to the end of OBJECT.\n\
593 If the value is non-nil, it is a position greater than POS, never equal.\n\n\
594 If the optional third argument LIMIT is non-nil, don't search\n\
595 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
597 Lisp_Object pos
, object
, limit
;
599 register INTERVAL i
, next
;
602 XSET (object
, Lisp_Buffer
, current_buffer
);
604 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
605 if (NULL_INTERVAL_P (i
))
608 next
= next_interval (i
);
609 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
610 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
611 next
= next_interval (next
);
613 if (NULL_INTERVAL_P (next
))
615 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
618 return next
->position
- (XTYPE (object
) == Lisp_String
);
621 /* Return 1 if there's a change in some property between BEG and END. */
624 property_change_between_p (beg
, end
)
627 register INTERVAL i
, next
;
628 Lisp_Object object
, pos
;
630 XSET (object
, Lisp_Buffer
, current_buffer
);
631 XFASTINT (pos
) = beg
;
633 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
634 if (NULL_INTERVAL_P (i
))
637 next
= next_interval (i
);
638 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
640 next
= next_interval (next
);
641 if (NULL_INTERVAL_P (next
))
643 if (next
->position
>= end
)
647 if (NULL_INTERVAL_P (next
))
653 DEFUN ("next-single-property-change", Fnext_single_property_change
,
654 Snext_single_property_change
, 2, 4, 0,
655 "Return the position of next property change for a specific property.\n\
656 Scans characters forward from POS till it finds\n\
657 a change in the PROP property, then returns the position of the change.\n\
658 The optional third argument OBJECT is the string or buffer to scan.\n\
659 The property values are compared with `eq'.\n\
660 Return nil if the property is constant all the way to the end of OBJECT.\n\
661 If the value is non-nil, it is a position greater than POS, never equal.\n\n\
662 If the optional fourth argument LIMIT is non-nil, don't search\n\
663 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
664 (pos
, prop
, object
, limit
)
665 Lisp_Object pos
, prop
, object
, limit
;
667 register INTERVAL i
, next
;
668 register Lisp_Object here_val
;
671 XSET (object
, Lisp_Buffer
, current_buffer
);
673 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
674 if (NULL_INTERVAL_P (i
))
677 here_val
= textget (i
->plist
, prop
);
678 next
= next_interval (i
);
679 while (! NULL_INTERVAL_P (next
)
680 && EQ (here_val
, textget (next
->plist
, prop
))
681 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
682 next
= next_interval (next
);
684 if (NULL_INTERVAL_P (next
))
686 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
689 return next
->position
- (XTYPE (object
) == Lisp_String
);
692 DEFUN ("previous-property-change", Fprevious_property_change
,
693 Sprevious_property_change
, 1, 3, 0,
694 "Return the position of previous property change.\n\
695 Scans characters backwards from POS in OBJECT till it finds\n\
696 a change in some text property, then returns the position of the change.\n\
697 The optional second argument OBJECT is the string or buffer to scan.\n\
698 Return nil if the property is constant all the way to the start of OBJECT.\n\
699 If the value is non-nil, it is a position less than POS, never equal.\n\n\
700 If the optional third argument LIMIT is non-nil, don't search\n\
701 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
703 Lisp_Object pos
, object
, limit
;
705 register INTERVAL i
, previous
;
708 XSET (object
, Lisp_Buffer
, current_buffer
);
710 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
711 if (NULL_INTERVAL_P (i
))
714 /* Start with the interval containing the char before point. */
715 if (i
->position
== XFASTINT (pos
))
716 i
= previous_interval (i
);
718 previous
= previous_interval (i
);
719 while (! NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
721 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
722 previous
= previous_interval (previous
);
723 if (NULL_INTERVAL_P (previous
))
726 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
729 return (previous
->position
+ LENGTH (previous
)
730 - (XTYPE (object
) == Lisp_String
));
733 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
734 Sprevious_single_property_change
, 2, 4, 0,
735 "Return the position of previous property change for a specific property.\n\
736 Scans characters backward from POS till it finds\n\
737 a change in the PROP property, then returns the position of the change.\n\
738 The optional third argument OBJECT is the string or buffer to scan.\n\
739 The property values are compared with `eq'.\n\
740 Return nil if the property is constant all the way to the start of OBJECT.\n\
741 If the value is non-nil, it is a position less than POS, never equal.\n\n\
742 If the optional fourth argument LIMIT is non-nil, don't search\n\
743 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
744 (pos
, prop
, object
, limit
)
745 Lisp_Object pos
, prop
, object
, limit
;
747 register INTERVAL i
, previous
;
748 register Lisp_Object here_val
;
751 XSET (object
, Lisp_Buffer
, current_buffer
);
753 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
754 if (NULL_INTERVAL_P (i
))
757 /* Start with the interval containing the char before point. */
758 if (i
->position
== XFASTINT (pos
))
759 i
= previous_interval (i
);
761 here_val
= textget (i
->plist
, prop
);
762 previous
= previous_interval (i
);
763 while (! NULL_INTERVAL_P (previous
)
764 && EQ (here_val
, textget (previous
->plist
, prop
))
766 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
767 previous
= previous_interval (previous
);
768 if (NULL_INTERVAL_P (previous
))
771 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
774 return (previous
->position
+ LENGTH (previous
)
775 - (XTYPE (object
) == Lisp_String
));
778 DEFUN ("add-text-properties", Fadd_text_properties
,
779 Sadd_text_properties
, 3, 4, 0,
780 "Add properties to the text from START to END.\n\
781 The third argument PROPS is a property list\n\
782 specifying the property values to add.\n\
783 The optional fourth argument, OBJECT,\n\
784 is the string or buffer containing the text.\n\
785 Return t if any property value actually changed, nil otherwise.")
786 (start
, end
, properties
, object
)
787 Lisp_Object start
, end
, properties
, object
;
789 register INTERVAL i
, unchanged
;
790 register int s
, len
, modified
= 0;
792 properties
= validate_plist (properties
);
793 if (NILP (properties
))
797 XSET (object
, Lisp_Buffer
, current_buffer
);
799 i
= validate_interval_range (object
, &start
, &end
, hard
);
800 if (NULL_INTERVAL_P (i
))
804 len
= XINT (end
) - s
;
806 /* If we're not starting on an interval boundary, we have to
807 split this interval. */
808 if (i
->position
!= s
)
810 /* If this interval already has the properties, we can
812 if (interval_has_all_properties (properties
, i
))
814 int got
= (LENGTH (i
) - (s
- i
->position
));
818 i
= next_interval (i
);
823 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
824 copy_properties (unchanged
, i
);
828 /* We are at the beginning of interval I, with LEN chars to scan. */
834 if (LENGTH (i
) >= len
)
836 if (interval_has_all_properties (properties
, i
))
837 return modified
? Qt
: Qnil
;
839 if (LENGTH (i
) == len
)
841 add_properties (properties
, i
, object
);
845 /* i doesn't have the properties, and goes past the change limit */
847 i
= split_interval_left (unchanged
, len
);
848 copy_properties (unchanged
, i
);
849 add_properties (properties
, i
, object
);
854 modified
+= add_properties (properties
, i
, object
);
855 i
= next_interval (i
);
859 DEFUN ("put-text-property", Fput_text_property
,
860 Sput_text_property
, 4, 5, 0,
861 "Set one property of the text from START to END.\n\
862 The third and fourth arguments PROP and VALUE\n\
863 specify the property to add.\n\
864 The optional fifth argument, OBJECT,\n\
865 is the string or buffer containing the text.")
866 (start
, end
, prop
, value
, object
)
867 Lisp_Object start
, end
, prop
, value
, object
;
869 Fadd_text_properties (start
, end
,
870 Fcons (prop
, Fcons (value
, Qnil
)),
875 DEFUN ("set-text-properties", Fset_text_properties
,
876 Sset_text_properties
, 3, 4, 0,
877 "Completely replace properties of text from START to END.\n\
878 The third argument PROPS is the new property list.\n\
879 The optional fourth argument, OBJECT,\n\
880 is the string or buffer containing the text.")
881 (start
, end
, props
, object
)
882 Lisp_Object start
, end
, props
, object
;
884 register INTERVAL i
, unchanged
;
885 register INTERVAL prev_changed
= NULL_INTERVAL
;
888 props
= validate_plist (props
);
891 XSET (object
, Lisp_Buffer
, current_buffer
);
893 i
= validate_interval_range (object
, &start
, &end
, hard
);
894 if (NULL_INTERVAL_P (i
))
898 len
= XINT (end
) - s
;
900 if (i
->position
!= s
)
903 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
905 if (LENGTH (i
) > len
)
907 copy_properties (unchanged
, i
);
908 i
= split_interval_left (i
, len
);
909 set_properties (props
, i
, object
);
913 set_properties (props
, i
, object
);
915 if (LENGTH (i
) == len
)
920 i
= next_interval (i
);
923 /* We are starting at the beginning of an interval, I */
929 if (LENGTH (i
) >= len
)
931 if (LENGTH (i
) > len
)
932 i
= split_interval_left (i
, len
);
934 if (NULL_INTERVAL_P (prev_changed
))
935 set_properties (props
, i
, object
);
937 merge_interval_left (i
);
942 if (NULL_INTERVAL_P (prev_changed
))
944 set_properties (props
, i
, object
);
948 prev_changed
= i
= merge_interval_left (i
);
950 i
= next_interval (i
);
956 DEFUN ("remove-text-properties", Fremove_text_properties
,
957 Sremove_text_properties
, 3, 4, 0,
958 "Remove some properties from text from START to END.\n\
959 The third argument PROPS is a property list\n\
960 whose property names specify the properties to remove.\n\
961 \(The values stored in PROPS are ignored.)\n\
962 The optional fourth argument, OBJECT,\n\
963 is the string or buffer containing the text.\n\
964 Return t if any property was actually removed, nil otherwise.")
965 (start
, end
, props
, object
)
966 Lisp_Object start
, end
, props
, object
;
968 register INTERVAL i
, unchanged
;
969 register int s
, len
, modified
= 0;
972 XSET (object
, Lisp_Buffer
, current_buffer
);
974 i
= validate_interval_range (object
, &start
, &end
, soft
);
975 if (NULL_INTERVAL_P (i
))
979 len
= XINT (end
) - s
;
981 if (i
->position
!= s
)
983 /* No properties on this first interval -- return if
984 it covers the entire region. */
985 if (! interval_has_some_properties (props
, i
))
987 int got
= (LENGTH (i
) - (s
- i
->position
));
991 i
= next_interval (i
);
993 /* Split away the beginning of this interval; what we don't
998 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
999 copy_properties (unchanged
, i
);
1003 /* We are at the beginning of an interval, with len to scan */
1009 if (LENGTH (i
) >= len
)
1011 if (! interval_has_some_properties (props
, i
))
1012 return modified
? Qt
: Qnil
;
1014 if (LENGTH (i
) == len
)
1016 remove_properties (props
, i
, object
);
1020 /* i has the properties, and goes past the change limit */
1022 i
= split_interval_left (i
, len
);
1023 copy_properties (unchanged
, i
);
1024 remove_properties (props
, i
, object
);
1029 modified
+= remove_properties (props
, i
, object
);
1030 i
= next_interval (i
);
1034 DEFUN ("text-property-any", Ftext_property_any
,
1035 Stext_property_any
, 4, 5, 0,
1036 "Check text from START to END to see if PROP is ever `eq' to VALUE.\n\
1037 If so, return the position of the first character whose PROP is `eq'\n\
1038 to VALUE. Otherwise return nil.\n\
1039 The optional fifth argument, OBJECT, is the string or buffer\n\
1040 containing the text.")
1041 (start
, end
, prop
, value
, object
)
1042 Lisp_Object start
, end
, prop
, value
, object
;
1044 register INTERVAL i
;
1045 register int e
, pos
;
1048 XSET (object
, Lisp_Buffer
, current_buffer
);
1049 i
= validate_interval_range (object
, &start
, &end
, soft
);
1052 while (! NULL_INTERVAL_P (i
))
1054 if (i
->position
>= e
)
1056 if (EQ (textget (i
->plist
, prop
), value
))
1059 if (pos
< XINT (start
))
1061 return make_number (pos
- (XTYPE (object
) == Lisp_String
));
1063 i
= next_interval (i
);
1068 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1069 Stext_property_not_all
, 4, 5, 0,
1070 "Check text from START to END to see if PROP is ever not `eq' to VALUE.\n\
1071 If so, return the position of the first character whose PROP is not\n\
1072 `eq' to VALUE. Otherwise, return nil.\n\
1073 The optional fifth argument, OBJECT, is the string or buffer\n\
1074 containing the text.")
1075 (start
, end
, prop
, value
, object
)
1076 Lisp_Object start
, end
, prop
, value
, object
;
1078 register INTERVAL i
;
1082 XSET (object
, Lisp_Buffer
, current_buffer
);
1083 i
= validate_interval_range (object
, &start
, &end
, soft
);
1084 if (NULL_INTERVAL_P (i
))
1085 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1089 while (! NULL_INTERVAL_P (i
))
1091 if (i
->position
>= e
)
1093 if (! EQ (textget (i
->plist
, prop
), value
))
1095 if (i
->position
> s
)
1097 return make_number (s
- (XTYPE (object
) == Lisp_String
));
1099 i
= next_interval (i
);
1104 #if 0 /* You can use set-text-properties for this. */
1106 DEFUN ("erase-text-properties", Ferase_text_properties
,
1107 Serase_text_properties
, 2, 3, 0,
1108 "Remove all properties from the text from START to END.\n\
1109 The optional third argument, OBJECT,\n\
1110 is the string or buffer containing the text.")
1111 (start
, end
, object
)
1112 Lisp_Object start
, end
, object
;
1114 register INTERVAL i
;
1115 register INTERVAL prev_changed
= NULL_INTERVAL
;
1116 register int s
, len
, modified
;
1119 XSET (object
, Lisp_Buffer
, current_buffer
);
1121 i
= validate_interval_range (object
, &start
, &end
, soft
);
1122 if (NULL_INTERVAL_P (i
))
1126 len
= XINT (end
) - s
;
1128 if (i
->position
!= s
)
1131 register INTERVAL unchanged
= i
;
1133 /* If there are properties here, then this text will be modified. */
1134 if (! NILP (i
->plist
))
1136 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1140 if (LENGTH (i
) > len
)
1142 i
= split_interval_right (i
, len
);
1143 copy_properties (unchanged
, i
);
1147 if (LENGTH (i
) == len
)
1152 /* If the text of I is without any properties, and contains
1153 LEN or more characters, then we may return without changing
1155 else if (LENGTH (i
) - (s
- i
->position
) <= len
)
1157 /* The amount of text to change extends past I, so just note
1158 how much we've gotten. */
1160 got
= LENGTH (i
) - (s
- i
->position
);
1164 i
= next_interval (i
);
1167 /* We are starting at the beginning of an interval, I. */
1170 if (LENGTH (i
) >= len
)
1172 /* If I has no properties, simply merge it if possible. */
1173 if (NILP (i
->plist
))
1175 if (! NULL_INTERVAL_P (prev_changed
))
1176 merge_interval_left (i
);
1178 return modified
? Qt
: Qnil
;
1181 if (LENGTH (i
) > len
)
1182 i
= split_interval_left (i
, len
);
1183 if (! NULL_INTERVAL_P (prev_changed
))
1184 merge_interval_left (i
);
1191 /* Here if we still need to erase past the end of I */
1193 if (NULL_INTERVAL_P (prev_changed
))
1195 modified
+= erase_properties (i
);
1200 modified
+= ! NILP (i
->plist
);
1201 /* Merging I will give it the properties of PREV_CHANGED. */
1202 prev_changed
= i
= merge_interval_left (i
);
1205 i
= next_interval (i
);
1208 return modified
? Qt
: Qnil
;
1212 /* I don't think this is the right interface to export; how often do you
1213 want to do something like this, other than when you're copying objects
1216 I think it would be better to have a pair of functions, one which
1217 returns the text properties of a region as a list of ranges and
1218 plists, and another which applies such a list to another object. */
1220 /* DEFUN ("copy-text-properties", Fcopy_text_properties,
1221 Scopy_text_properties, 5, 6, 0,
1222 "Add properties from SRC-START to SRC-END of SRC at DEST-POS of DEST.\n\
1223 SRC and DEST may each refer to strings or buffers.\n\
1224 Optional sixth argument PROP causes only that property to be copied.\n\
1225 Properties are copied to DEST as if by `add-text-properties'.\n\
1226 Return t if any property value actually changed, nil otherwise.") */
1229 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1230 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1236 int s
, e
, e2
, p
, len
, modified
= 0;
1238 i
= validate_interval_range (src
, &start
, &end
, soft
);
1239 if (NULL_INTERVAL_P (i
))
1242 CHECK_NUMBER_COERCE_MARKER (pos
, 0);
1244 Lisp_Object dest_start
, dest_end
;
1247 XFASTINT (dest_end
) = XINT (dest_start
) + (XINT (end
) - XINT (start
));
1248 /* Apply this to a copy of pos; it will try to increment its arguments,
1249 which we don't want. */
1250 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1261 e2
= i
->position
+ LENGTH (i
);
1268 while (! NILP (plist
))
1270 if (EQ (Fcar (plist
), prop
))
1272 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1275 plist
= Fcdr (Fcdr (plist
));
1279 /* Must defer modifications to the interval tree in case src
1280 and dest refer to the same string or buffer. */
1281 stuff
= Fcons (Fcons (make_number (p
),
1282 Fcons (make_number (p
+ len
),
1283 Fcons (plist
, Qnil
))),
1287 i
= next_interval (i
);
1288 if (NULL_INTERVAL_P (i
))
1295 while (! NILP (stuff
))
1298 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1299 Fcar (Fcdr (Fcdr (res
))), dest
);
1302 stuff
= Fcdr (stuff
);
1305 return modified
? Qt
: Qnil
;
1311 DEFVAR_INT ("interval-balance-threshold", &interval_balance_threshold
,
1312 "Threshold for rebalancing interval trees, expressed as the\n\
1313 percentage by which the left interval tree should not differ from the right.");
1314 interval_balance_threshold
= 8;
1316 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
1317 "If non-nil, don't call the text property values of\n\
1318 `point-left' and `point-entered'.");
1319 Vinhibit_point_motion_hooks
= Qnil
;
1321 /* Common attributes one might give text */
1323 staticpro (&Qforeground
);
1324 Qforeground
= intern ("foreground");
1325 staticpro (&Qbackground
);
1326 Qbackground
= intern ("background");
1328 Qfont
= intern ("font");
1329 staticpro (&Qstipple
);
1330 Qstipple
= intern ("stipple");
1331 staticpro (&Qunderline
);
1332 Qunderline
= intern ("underline");
1333 staticpro (&Qread_only
);
1334 Qread_only
= intern ("read-only");
1335 staticpro (&Qinvisible
);
1336 Qinvisible
= intern ("invisible");
1337 staticpro (&Qintangible
);
1338 Qintangible
= intern ("intangible");
1339 staticpro (&Qcategory
);
1340 Qcategory
= intern ("category");
1341 staticpro (&Qlocal_map
);
1342 Qlocal_map
= intern ("local-map");
1343 staticpro (&Qfront_sticky
);
1344 Qfront_sticky
= intern ("front-sticky");
1345 staticpro (&Qrear_nonsticky
);
1346 Qrear_nonsticky
= intern ("rear-nonsticky");
1348 /* Properties that text might use to specify certain actions */
1350 staticpro (&Qmouse_left
);
1351 Qmouse_left
= intern ("mouse-left");
1352 staticpro (&Qmouse_entered
);
1353 Qmouse_entered
= intern ("mouse-entered");
1354 staticpro (&Qpoint_left
);
1355 Qpoint_left
= intern ("point-left");
1356 staticpro (&Qpoint_entered
);
1357 Qpoint_entered
= intern ("point-entered");
1359 defsubr (&Stext_properties_at
);
1360 defsubr (&Sget_text_property
);
1361 defsubr (&Snext_property_change
);
1362 defsubr (&Snext_single_property_change
);
1363 defsubr (&Sprevious_property_change
);
1364 defsubr (&Sprevious_single_property_change
);
1365 defsubr (&Sadd_text_properties
);
1366 defsubr (&Sput_text_property
);
1367 defsubr (&Sset_text_properties
);
1368 defsubr (&Sremove_text_properties
);
1369 defsubr (&Stext_property_any
);
1370 defsubr (&Stext_property_not_all
);
1371 /* defsubr (&Serase_text_properties); */
1372 /* defsubr (&Scopy_text_properties); */
1377 lose
-- this shouldn
't be compiled if USE_TEXT_PROPERTIES isn't defined
1379 #endif /* USE_TEXT_PROPERTIES */