(parse_single_submenu): Use individual keymap's prompt
[emacs.git] / src / textprop.c
blob745f6b7fb909c81335b64f00a98f482131466ee3
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 #include <config.h>
23 #include "lisp.h"
24 #include "intervals.h"
25 #include "buffer.h"
26 #include "window.h"
28 #ifndef NULL
29 #define NULL (void *)0
30 #endif
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
33 universal set. */
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
38 /* NOTES: previous- and next- property change will have to skip
39 zero-length intervals if they are implemented. This could be done
40 inside next_interval and previous_interval.
42 set_properties needs to deal with the interval property cache.
44 It is assumed that for any interval plist, a property appears
45 only once on the list. Although some code i.e., remove_properties,
46 handles the more general case, the uniqueness of properties is
47 necessary for the system to remain consistent. This requirement
48 is enforced by the subrs installing properties onto the intervals. */
51 /* Types of hooks. */
52 Lisp_Object Qmouse_left;
53 Lisp_Object Qmouse_entered;
54 Lisp_Object Qpoint_left;
55 Lisp_Object Qpoint_entered;
56 Lisp_Object Qcategory;
57 Lisp_Object Qlocal_map;
59 /* Visual properties text (including strings) may have. */
60 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
61 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky, Qrear_nonsticky;
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
68 traversing plists. */
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
71 Lisp_Object Vinhibit_point_motion_hooks;
72 Lisp_Object Vdefault_text_properties;
73 Lisp_Object Vchar_property_alias_alist;
74 Lisp_Object Vtext_property_default_nonsticky;
76 /* verify_interval_modification saves insertion hooks here
77 to be run later by report_interval_modification. */
78 Lisp_Object interval_insert_behind_hooks;
79 Lisp_Object interval_insert_in_front_hooks;
82 /* Signal a `text-read-only' error. This function makes it easier
83 to capture that error in GDB by putting a breakpoint on it. */
85 static void
86 text_read_only (propval)
87 Lisp_Object propval;
89 Fsignal (Qtext_read_only, STRINGP (propval) ? Fcons (propval, Qnil) : Qnil);
94 /* Extract the interval at the position pointed to by BEGIN from
95 OBJECT, a string or buffer. Additionally, check that the positions
96 pointed to by BEGIN and END are within the bounds of OBJECT, and
97 reverse them if *BEGIN is greater than *END. The objects pointed
98 to by BEGIN and END may be integers or markers; if the latter, they
99 are coerced to integers.
101 When OBJECT is a string, we increment *BEGIN and *END
102 to make them origin-one.
104 Note that buffer points don't correspond to interval indices.
105 For example, point-max is 1 greater than the index of the last
106 character. This difference is handled in the caller, which uses
107 the validated points to determine a length, and operates on that.
108 Exceptions are Ftext_properties_at, Fnext_property_change, and
109 Fprevious_property_change which call this function with BEGIN == END.
110 Handle this case specially.
112 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
113 create an interval tree for OBJECT if one doesn't exist, provided
114 the object actually contains text. In the current design, if there
115 is no text, there can be no text properties. */
117 #define soft 0
118 #define hard 1
120 INTERVAL
121 validate_interval_range (object, begin, end, force)
122 Lisp_Object object, *begin, *end;
123 int force;
125 register INTERVAL i;
126 int searchpos;
128 CHECK_STRING_OR_BUFFER (object);
129 CHECK_NUMBER_COERCE_MARKER (*begin);
130 CHECK_NUMBER_COERCE_MARKER (*end);
132 /* If we are asked for a point, but from a subr which operates
133 on a range, then return nothing. */
134 if (EQ (*begin, *end) && begin != end)
135 return NULL_INTERVAL;
137 if (XINT (*begin) > XINT (*end))
139 Lisp_Object n;
140 n = *begin;
141 *begin = *end;
142 *end = n;
145 if (BUFFERP (object))
147 register struct buffer *b = XBUFFER (object);
149 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
150 && XINT (*end) <= BUF_ZV (b)))
151 args_out_of_range (*begin, *end);
152 i = BUF_INTERVALS (b);
154 /* If there's no text, there are no properties. */
155 if (BUF_BEGV (b) == BUF_ZV (b))
156 return NULL_INTERVAL;
158 searchpos = XINT (*begin);
160 else
162 int len = SCHARS (object);
164 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
165 && XINT (*end) <= len))
166 args_out_of_range (*begin, *end);
167 XSETFASTINT (*begin, XFASTINT (*begin));
168 if (begin != end)
169 XSETFASTINT (*end, XFASTINT (*end));
170 i = STRING_INTERVALS (object);
172 if (len == 0)
173 return NULL_INTERVAL;
175 searchpos = XINT (*begin);
178 if (NULL_INTERVAL_P (i))
179 return (force ? create_root_interval (object) : i);
181 return find_interval (i, searchpos);
184 /* Validate LIST as a property list. If LIST is not a list, then
185 make one consisting of (LIST nil). Otherwise, verify that LIST
186 is even numbered and thus suitable as a plist. */
188 static Lisp_Object
189 validate_plist (list)
190 Lisp_Object list;
192 if (NILP (list))
193 return Qnil;
195 if (CONSP (list))
197 register int i;
198 register Lisp_Object tail;
199 for (i = 0, tail = list; !NILP (tail); i++)
201 tail = Fcdr (tail);
202 QUIT;
204 if (i & 1)
205 error ("Odd length text property list");
206 return list;
209 return Fcons (list, Fcons (Qnil, Qnil));
212 /* Return nonzero if interval I has all the properties,
213 with the same values, of list PLIST. */
215 static int
216 interval_has_all_properties (plist, i)
217 Lisp_Object plist;
218 INTERVAL i;
220 register Lisp_Object tail1, tail2, sym1;
221 register int found;
223 /* Go through each element of PLIST. */
224 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
226 sym1 = Fcar (tail1);
227 found = 0;
229 /* Go through I's plist, looking for sym1 */
230 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
231 if (EQ (sym1, Fcar (tail2)))
233 /* Found the same property on both lists. If the
234 values are unequal, return zero. */
235 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
236 return 0;
238 /* Property has same value on both lists; go to next one. */
239 found = 1;
240 break;
243 if (! found)
244 return 0;
247 return 1;
250 /* Return nonzero if the plist of interval I has any of the
251 properties of PLIST, regardless of their values. */
253 static INLINE int
254 interval_has_some_properties (plist, i)
255 Lisp_Object plist;
256 INTERVAL i;
258 register Lisp_Object tail1, tail2, sym;
260 /* Go through each element of PLIST. */
261 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
263 sym = Fcar (tail1);
265 /* Go through i's plist, looking for tail1 */
266 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
267 if (EQ (sym, Fcar (tail2)))
268 return 1;
271 return 0;
274 /* Return nonzero if the plist of interval I has any of the
275 property names in LIST, regardless of their values. */
277 static INLINE int
278 interval_has_some_properties_list (list, i)
279 Lisp_Object list;
280 INTERVAL i;
282 register Lisp_Object tail1, tail2, sym;
284 /* Go through each element of LIST. */
285 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
287 sym = Fcar (tail1);
289 /* Go through i's plist, looking for tail1 */
290 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
291 if (EQ (sym, XCAR (tail2)))
292 return 1;
295 return 0;
298 /* Changing the plists of individual intervals. */
300 /* Return the value of PROP in property-list PLIST, or Qunbound if it
301 has none. */
302 static Lisp_Object
303 property_value (plist, prop)
304 Lisp_Object plist, prop;
306 Lisp_Object value;
308 while (PLIST_ELT_P (plist, value))
309 if (EQ (XCAR (plist), prop))
310 return XCAR (value);
311 else
312 plist = XCDR (value);
314 return Qunbound;
317 /* Set the properties of INTERVAL to PROPERTIES,
318 and record undo info for the previous values.
319 OBJECT is the string or buffer that INTERVAL belongs to. */
321 static void
322 set_properties (properties, interval, object)
323 Lisp_Object properties, object;
324 INTERVAL interval;
326 Lisp_Object sym, value;
328 if (BUFFERP (object))
330 /* For each property in the old plist which is missing from PROPERTIES,
331 or has a different value in PROPERTIES, make an undo record. */
332 for (sym = interval->plist;
333 PLIST_ELT_P (sym, value);
334 sym = XCDR (value))
335 if (! EQ (property_value (properties, XCAR (sym)),
336 XCAR (value)))
338 record_property_change (interval->position, LENGTH (interval),
339 XCAR (sym), XCAR (value),
340 object);
343 /* For each new property that has no value at all in the old plist,
344 make an undo record binding it to nil, so it will be removed. */
345 for (sym = properties;
346 PLIST_ELT_P (sym, value);
347 sym = XCDR (value))
348 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
350 record_property_change (interval->position, LENGTH (interval),
351 XCAR (sym), Qnil,
352 object);
356 /* Store new properties. */
357 interval->plist = Fcopy_sequence (properties);
360 /* Add the properties of PLIST to the interval I, or set
361 the value of I's property to the value of the property on PLIST
362 if they are different.
364 OBJECT should be the string or buffer the interval is in.
366 Return nonzero if this changes I (i.e., if any members of PLIST
367 are actually added to I's plist) */
369 static int
370 add_properties (plist, i, object)
371 Lisp_Object plist;
372 INTERVAL i;
373 Lisp_Object object;
375 Lisp_Object tail1, tail2, sym1, val1;
376 register int changed = 0;
377 register int found;
378 struct gcpro gcpro1, gcpro2, gcpro3;
380 tail1 = plist;
381 sym1 = Qnil;
382 val1 = Qnil;
383 /* No need to protect OBJECT, because we can GC only in the case
384 where it is a buffer, and live buffers are always protected.
385 I and its plist are also protected, via OBJECT. */
386 GCPRO3 (tail1, sym1, val1);
388 /* Go through each element of PLIST. */
389 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
391 sym1 = Fcar (tail1);
392 val1 = Fcar (Fcdr (tail1));
393 found = 0;
395 /* Go through I's plist, looking for sym1 */
396 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
397 if (EQ (sym1, Fcar (tail2)))
399 /* No need to gcpro, because tail2 protects this
400 and it must be a cons cell (we get an error otherwise). */
401 register Lisp_Object this_cdr;
403 this_cdr = Fcdr (tail2);
404 /* Found the property. Now check its value. */
405 found = 1;
407 /* The properties have the same value on both lists.
408 Continue to the next property. */
409 if (EQ (val1, Fcar (this_cdr)))
410 break;
412 /* Record this change in the buffer, for undo purposes. */
413 if (BUFFERP (object))
415 record_property_change (i->position, LENGTH (i),
416 sym1, Fcar (this_cdr), object);
419 /* I's property has a different value -- change it */
420 Fsetcar (this_cdr, val1);
421 changed++;
422 break;
425 if (! found)
427 /* Record this change in the buffer, for undo purposes. */
428 if (BUFFERP (object))
430 record_property_change (i->position, LENGTH (i),
431 sym1, Qnil, object);
433 i->plist = Fcons (sym1, Fcons (val1, i->plist));
434 changed++;
438 UNGCPRO;
440 return changed;
443 /* For any members of PLIST, or LIST,
444 which are properties of I, remove them from I's plist.
445 (If PLIST is non-nil, use that, otherwise use LIST.)
446 OBJECT is the string or buffer containing I. */
448 static int
449 remove_properties (plist, list, i, object)
450 Lisp_Object plist, list;
451 INTERVAL i;
452 Lisp_Object object;
454 register Lisp_Object tail1, tail2, sym, current_plist;
455 register int changed = 0;
457 /* Nonzero means tail1 is a plist, otherwise it is a list. */
458 int use_plist;
460 current_plist = i->plist;
462 if (! NILP (plist))
463 tail1 = plist, use_plist = 1;
464 else
465 tail1 = list, use_plist = 0;
467 /* Go through each element of LIST or PLIST. */
468 while (CONSP (tail1))
470 sym = XCAR (tail1);
472 /* First, remove the symbol if it's at the head of the list */
473 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
475 if (BUFFERP (object))
476 record_property_change (i->position, LENGTH (i),
477 sym, XCAR (XCDR (current_plist)),
478 object);
480 current_plist = XCDR (XCDR (current_plist));
481 changed++;
484 /* Go through I's plist, looking for SYM. */
485 tail2 = current_plist;
486 while (! NILP (tail2))
488 register Lisp_Object this;
489 this = XCDR (XCDR (tail2));
490 if (CONSP (this) && EQ (sym, XCAR (this)))
492 if (BUFFERP (object))
493 record_property_change (i->position, LENGTH (i),
494 sym, XCAR (XCDR (this)), object);
496 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
497 changed++;
499 tail2 = this;
502 /* Advance thru TAIL1 one way or the other. */
503 tail1 = XCDR (tail1);
504 if (use_plist && CONSP (tail1))
505 tail1 = XCDR (tail1);
508 if (changed)
509 i->plist = current_plist;
510 return changed;
513 #if 0
514 /* Remove all properties from interval I. Return non-zero
515 if this changes the interval. */
517 static INLINE int
518 erase_properties (i)
519 INTERVAL i;
521 if (NILP (i->plist))
522 return 0;
524 i->plist = Qnil;
525 return 1;
527 #endif
529 /* Returns the interval of POSITION in OBJECT.
530 POSITION is BEG-based. */
532 INTERVAL
533 interval_of (position, object)
534 int position;
535 Lisp_Object object;
537 register INTERVAL i;
538 int beg, end;
540 if (NILP (object))
541 XSETBUFFER (object, current_buffer);
542 else if (EQ (object, Qt))
543 return NULL_INTERVAL;
545 CHECK_STRING_OR_BUFFER (object);
547 if (BUFFERP (object))
549 register struct buffer *b = XBUFFER (object);
551 beg = BUF_BEGV (b);
552 end = BUF_ZV (b);
553 i = BUF_INTERVALS (b);
555 else
557 beg = 0;
558 end = SCHARS (object);
559 i = STRING_INTERVALS (object);
562 if (!(beg <= position && position <= end))
563 args_out_of_range (make_number (position), make_number (position));
564 if (beg == end || NULL_INTERVAL_P (i))
565 return NULL_INTERVAL;
567 return find_interval (i, position);
570 DEFUN ("text-properties-at", Ftext_properties_at,
571 Stext_properties_at, 1, 2, 0,
572 doc: /* Return the list of properties of the character at POSITION in OBJECT.
573 OBJECT is the string or buffer to look for the properties in;
574 nil means the current buffer.
575 If POSITION is at the end of OBJECT, the value is nil. */)
576 (position, object)
577 Lisp_Object position, object;
579 register INTERVAL i;
581 if (NILP (object))
582 XSETBUFFER (object, current_buffer);
584 i = validate_interval_range (object, &position, &position, soft);
585 if (NULL_INTERVAL_P (i))
586 return Qnil;
587 /* If POSITION is at the end of the interval,
588 it means it's the end of OBJECT.
589 There are no properties at the very end,
590 since no character follows. */
591 if (XINT (position) == LENGTH (i) + i->position)
592 return Qnil;
594 return i->plist;
597 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
598 doc: /* Return the value of POSITION's property PROP, in OBJECT.
599 OBJECT is optional and defaults to the current buffer.
600 If POSITION is at the end of OBJECT, the value is nil. */)
601 (position, prop, object)
602 Lisp_Object position, object;
603 Lisp_Object prop;
605 return textget (Ftext_properties_at (position, object), prop);
608 /* Return the value of char's property PROP, in OBJECT at POSITION.
609 OBJECT is optional and defaults to the current buffer.
610 If OVERLAY is non-0, then in the case that the returned property is from
611 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
612 returned in *OVERLAY.
613 If POSITION is at the end of OBJECT, the value is nil.
614 If OBJECT is a buffer, then overlay properties are considered as well as
615 text properties.
616 If OBJECT is a window, then that window's buffer is used, but
617 window-specific overlays are considered only if they are associated
618 with OBJECT. */
619 Lisp_Object
620 get_char_property_and_overlay (position, prop, object, overlay)
621 Lisp_Object position, object;
622 register Lisp_Object prop;
623 Lisp_Object *overlay;
625 struct window *w = 0;
627 CHECK_NUMBER_COERCE_MARKER (position);
629 if (NILP (object))
630 XSETBUFFER (object, current_buffer);
632 if (WINDOWP (object))
634 w = XWINDOW (object);
635 object = w->buffer;
637 if (BUFFERP (object))
639 int posn = XINT (position);
640 int noverlays;
641 Lisp_Object *overlay_vec, tem;
642 int len;
643 struct buffer *obuf = current_buffer;
645 set_buffer_temp (XBUFFER (object));
647 /* First try with room for 40 overlays. */
648 len = 40;
649 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
651 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
652 NULL, NULL, 0);
654 /* If there are more than 40,
655 make enough space for all, and try again. */
656 if (noverlays > len)
658 len = noverlays;
659 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
660 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
661 NULL, NULL, 0);
663 noverlays = sort_overlays (overlay_vec, noverlays, w);
665 set_buffer_temp (obuf);
667 /* Now check the overlays in order of decreasing priority. */
668 while (--noverlays >= 0)
670 tem = Foverlay_get (overlay_vec[noverlays], prop);
671 if (!NILP (tem))
673 if (overlay)
674 /* Return the overlay we got the property from. */
675 *overlay = overlay_vec[noverlays];
676 return tem;
681 if (overlay)
682 /* Indicate that the return value is not from an overlay. */
683 *overlay = Qnil;
685 /* Not a buffer, or no appropriate overlay, so fall through to the
686 simpler case. */
687 return Fget_text_property (position, prop, object);
690 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
691 doc: /* Return the value of POSITION's property PROP, in OBJECT.
692 Both overlay properties and text properties are checked.
693 OBJECT is optional and defaults to the current buffer.
694 If POSITION is at the end of OBJECT, the value is nil.
695 If OBJECT is a buffer, then overlay properties are considered as well as
696 text properties.
697 If OBJECT is a window, then that window's buffer is used, but window-specific
698 overlays are considered only if they are associated with OBJECT. */)
699 (position, prop, object)
700 Lisp_Object position, object;
701 register Lisp_Object prop;
703 return get_char_property_and_overlay (position, prop, object, 0);
706 DEFUN ("next-char-property-change", Fnext_char_property_change,
707 Snext_char_property_change, 1, 2, 0,
708 doc: /* Return the position of next text property or overlay change.
709 This scans characters forward from POSITION till it finds a change in
710 some text property, or the beginning or end of an overlay, and returns
711 the position of that.
712 If none is found, the function returns (point-max).
714 If the optional third argument LIMIT is non-nil, don't search
715 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
716 (position, limit)
717 Lisp_Object position, limit;
719 Lisp_Object temp;
721 temp = Fnext_overlay_change (position);
722 if (! NILP (limit))
724 CHECK_NUMBER (limit);
725 if (XINT (limit) < XINT (temp))
726 temp = limit;
728 return Fnext_property_change (position, Qnil, temp);
731 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
732 Sprevious_char_property_change, 1, 2, 0,
733 doc: /* Return the position of previous text property or overlay change.
734 Scans characters backward from POSITION till it finds a change in some
735 text property, or the beginning or end of an overlay, and returns the
736 position of that.
737 If none is found, the function returns (point-max).
739 If the optional third argument LIMIT is non-nil, don't search
740 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
741 (position, limit)
742 Lisp_Object position, limit;
744 Lisp_Object temp;
746 temp = Fprevious_overlay_change (position);
747 if (! NILP (limit))
749 CHECK_NUMBER (limit);
750 if (XINT (limit) > XINT (temp))
751 temp = limit;
753 return Fprevious_property_change (position, Qnil, temp);
757 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
758 Snext_single_char_property_change, 2, 4, 0,
759 doc: /* Return the position of next text property or overlay change for a specific property.
760 Scans characters forward from POSITION till it finds
761 a change in the PROP property, then returns the position of the change.
762 The optional third argument OBJECT is the string or buffer to scan.
763 The property values are compared with `eq'.
764 If the property is constant all the way to the end of OBJECT, return the
765 last valid position in OBJECT.
766 If the optional fourth argument LIMIT is non-nil, don't search
767 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
768 (position, prop, object, limit)
769 Lisp_Object prop, position, object, limit;
771 if (STRINGP (object))
773 position = Fnext_single_property_change (position, prop, object, limit);
774 if (NILP (position))
776 if (NILP (limit))
777 position = make_number (SCHARS (object));
778 else
779 position = limit;
782 else
784 Lisp_Object initial_value, value;
785 int count = SPECPDL_INDEX ();
787 if (! NILP (object))
788 CHECK_BUFFER (object);
790 if (BUFFERP (object) && current_buffer != XBUFFER (object))
792 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
793 Fset_buffer (object);
796 initial_value = Fget_char_property (position, prop, object);
798 if (NILP (limit))
799 XSETFASTINT (limit, BUF_ZV (current_buffer));
800 else
801 CHECK_NUMBER_COERCE_MARKER (limit);
803 for (;;)
805 position = Fnext_char_property_change (position, limit);
806 if (XFASTINT (position) >= XFASTINT (limit)) {
807 position = limit;
808 break;
811 value = Fget_char_property (position, prop, object);
812 if (!EQ (value, initial_value))
813 break;
816 unbind_to (count, Qnil);
819 return position;
822 DEFUN ("previous-single-char-property-change",
823 Fprevious_single_char_property_change,
824 Sprevious_single_char_property_change, 2, 4, 0,
825 doc: /* Return the position of previous text property or overlay change for a specific property.
826 Scans characters backward from POSITION till it finds
827 a change in the PROP property, then returns the position of the change.
828 The optional third argument OBJECT is the string or buffer to scan.
829 The property values are compared with `eq'.
830 If the property is constant all the way to the start of OBJECT, return the
831 first valid position in OBJECT.
832 If the optional fourth argument LIMIT is non-nil, don't search
833 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
834 (position, prop, object, limit)
835 Lisp_Object prop, position, object, limit;
837 if (STRINGP (object))
839 position = Fprevious_single_property_change (position, prop, object, limit);
840 if (NILP (position))
842 if (NILP (limit))
843 position = make_number (SCHARS (object));
844 else
845 position = limit;
848 else
850 int count = SPECPDL_INDEX ();
852 if (! NILP (object))
853 CHECK_BUFFER (object);
855 if (BUFFERP (object) && current_buffer != XBUFFER (object))
857 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
858 Fset_buffer (object);
861 if (NILP (limit))
862 XSETFASTINT (limit, BUF_BEGV (current_buffer));
863 else
864 CHECK_NUMBER_COERCE_MARKER (limit);
866 if (XFASTINT (position) <= XFASTINT (limit))
867 position = limit;
868 else
870 Lisp_Object initial_value =
871 Fget_char_property (make_number (XFASTINT (position) - 1),
872 prop, object);
874 for (;;)
876 position = Fprevious_char_property_change (position, limit);
878 if (XFASTINT (position) <= XFASTINT (limit))
880 position = limit;
881 break;
883 else
885 Lisp_Object value =
886 Fget_char_property (make_number (XFASTINT (position) - 1),
887 prop, object);
889 if (!EQ (value, initial_value))
890 break;
895 unbind_to (count, Qnil);
898 return position;
901 DEFUN ("next-property-change", Fnext_property_change,
902 Snext_property_change, 1, 3, 0,
903 doc: /* Return the position of next property change.
904 Scans characters forward from POSITION in OBJECT till it finds
905 a change in some text property, then returns the position of the change.
906 The optional second argument OBJECT is the string or buffer to scan.
907 Return nil if the property is constant all the way to the end of OBJECT.
908 If the value is non-nil, it is a position greater than POSITION, never equal.
910 If the optional third argument LIMIT is non-nil, don't search
911 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
912 (position, object, limit)
913 Lisp_Object position, object, limit;
915 register INTERVAL i, next;
917 if (NILP (object))
918 XSETBUFFER (object, current_buffer);
920 if (!NILP (limit) && !EQ (limit, Qt))
921 CHECK_NUMBER_COERCE_MARKER (limit);
923 i = validate_interval_range (object, &position, &position, soft);
925 /* If LIMIT is t, return start of next interval--don't
926 bother checking further intervals. */
927 if (EQ (limit, Qt))
929 if (NULL_INTERVAL_P (i))
930 next = i;
931 else
932 next = next_interval (i);
934 if (NULL_INTERVAL_P (next))
935 XSETFASTINT (position, (STRINGP (object)
936 ? SCHARS (object)
937 : BUF_ZV (XBUFFER (object))));
938 else
939 XSETFASTINT (position, next->position);
940 return position;
943 if (NULL_INTERVAL_P (i))
944 return limit;
946 next = next_interval (i);
948 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
949 && (NILP (limit) || next->position < XFASTINT (limit)))
950 next = next_interval (next);
952 if (NULL_INTERVAL_P (next))
953 return limit;
954 if (NILP (limit))
955 XSETFASTINT (limit, (STRINGP (object)
956 ? SCHARS (object)
957 : BUF_ZV (XBUFFER (object))));
958 if (!(next->position < XFASTINT (limit)))
959 return limit;
961 XSETFASTINT (position, next->position);
962 return position;
965 /* Return 1 if there's a change in some property between BEG and END. */
968 property_change_between_p (beg, end)
969 int beg, end;
971 register INTERVAL i, next;
972 Lisp_Object object, pos;
974 XSETBUFFER (object, current_buffer);
975 XSETFASTINT (pos, beg);
977 i = validate_interval_range (object, &pos, &pos, soft);
978 if (NULL_INTERVAL_P (i))
979 return 0;
981 next = next_interval (i);
982 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
984 next = next_interval (next);
985 if (NULL_INTERVAL_P (next))
986 return 0;
987 if (next->position >= end)
988 return 0;
991 if (NULL_INTERVAL_P (next))
992 return 0;
994 return 1;
997 DEFUN ("next-single-property-change", Fnext_single_property_change,
998 Snext_single_property_change, 2, 4, 0,
999 doc: /* Return the position of next property change for a specific property.
1000 Scans characters forward from POSITION till it finds
1001 a change in the PROP property, then returns the position of the change.
1002 The optional third argument OBJECT is the string or buffer to scan.
1003 The property values are compared with `eq'.
1004 Return nil if the property is constant all the way to the end of OBJECT.
1005 If the value is non-nil, it is a position greater than POSITION, never equal.
1007 If the optional fourth argument LIMIT is non-nil, don't search
1008 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1009 (position, prop, object, limit)
1010 Lisp_Object position, prop, object, limit;
1012 register INTERVAL i, next;
1013 register Lisp_Object here_val;
1015 if (NILP (object))
1016 XSETBUFFER (object, current_buffer);
1018 if (!NILP (limit))
1019 CHECK_NUMBER_COERCE_MARKER (limit);
1021 i = validate_interval_range (object, &position, &position, soft);
1022 if (NULL_INTERVAL_P (i))
1023 return limit;
1025 here_val = textget (i->plist, prop);
1026 next = next_interval (i);
1027 while (! NULL_INTERVAL_P (next)
1028 && EQ (here_val, textget (next->plist, prop))
1029 && (NILP (limit) || next->position < XFASTINT (limit)))
1030 next = next_interval (next);
1032 if (NULL_INTERVAL_P (next))
1033 return limit;
1034 if (NILP (limit))
1035 XSETFASTINT (limit, (STRINGP (object)
1036 ? SCHARS (object)
1037 : BUF_ZV (XBUFFER (object))));
1038 if (!(next->position < XFASTINT (limit)))
1039 return limit;
1041 return make_number (next->position);
1044 DEFUN ("previous-property-change", Fprevious_property_change,
1045 Sprevious_property_change, 1, 3, 0,
1046 doc: /* Return the position of previous property change.
1047 Scans characters backwards from POSITION in OBJECT till it finds
1048 a change in some text property, then returns the position of the change.
1049 The optional second argument OBJECT is the string or buffer to scan.
1050 Return nil if the property is constant all the way to the start of OBJECT.
1051 If the value is non-nil, it is a position less than POSITION, never equal.
1053 If the optional third argument LIMIT is non-nil, don't search
1054 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1055 (position, object, limit)
1056 Lisp_Object position, object, limit;
1058 register INTERVAL i, previous;
1060 if (NILP (object))
1061 XSETBUFFER (object, current_buffer);
1063 if (!NILP (limit))
1064 CHECK_NUMBER_COERCE_MARKER (limit);
1066 i = validate_interval_range (object, &position, &position, soft);
1067 if (NULL_INTERVAL_P (i))
1068 return limit;
1070 /* Start with the interval containing the char before point. */
1071 if (i->position == XFASTINT (position))
1072 i = previous_interval (i);
1074 previous = previous_interval (i);
1075 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1076 && (NILP (limit)
1077 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1078 previous = previous_interval (previous);
1079 if (NULL_INTERVAL_P (previous))
1080 return limit;
1081 if (NILP (limit))
1082 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1083 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1084 return limit;
1086 return make_number (previous->position + LENGTH (previous));
1089 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1090 Sprevious_single_property_change, 2, 4, 0,
1091 doc: /* Return the position of previous property change for a specific property.
1092 Scans characters backward from POSITION till it finds
1093 a change in the PROP property, then returns the position of the change.
1094 The optional third argument OBJECT is the string or buffer to scan.
1095 The property values are compared with `eq'.
1096 Return nil if the property is constant all the way to the start of OBJECT.
1097 If the value is non-nil, it is a position less than POSITION, never equal.
1099 If the optional fourth argument LIMIT is non-nil, don't search
1100 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1101 (position, prop, object, limit)
1102 Lisp_Object position, prop, object, limit;
1104 register INTERVAL i, previous;
1105 register Lisp_Object here_val;
1107 if (NILP (object))
1108 XSETBUFFER (object, current_buffer);
1110 if (!NILP (limit))
1111 CHECK_NUMBER_COERCE_MARKER (limit);
1113 i = validate_interval_range (object, &position, &position, soft);
1115 /* Start with the interval containing the char before point. */
1116 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1117 i = previous_interval (i);
1119 if (NULL_INTERVAL_P (i))
1120 return limit;
1122 here_val = textget (i->plist, prop);
1123 previous = previous_interval (i);
1124 while (!NULL_INTERVAL_P (previous)
1125 && EQ (here_val, textget (previous->plist, prop))
1126 && (NILP (limit)
1127 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1128 previous = previous_interval (previous);
1129 if (NULL_INTERVAL_P (previous))
1130 return limit;
1131 if (NILP (limit))
1132 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1133 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1134 return limit;
1136 return make_number (previous->position + LENGTH (previous));
1139 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1141 DEFUN ("add-text-properties", Fadd_text_properties,
1142 Sadd_text_properties, 3, 4, 0,
1143 doc: /* Add properties to the text from START to END.
1144 The third argument PROPERTIES is a property list
1145 specifying the property values to add.
1146 The optional fourth argument, OBJECT,
1147 is the string or buffer containing the text.
1148 Return t if any property value actually changed, nil otherwise. */)
1149 (start, end, properties, object)
1150 Lisp_Object start, end, properties, object;
1152 register INTERVAL i, unchanged;
1153 register int s, len, modified = 0;
1154 struct gcpro gcpro1;
1156 properties = validate_plist (properties);
1157 if (NILP (properties))
1158 return Qnil;
1160 if (NILP (object))
1161 XSETBUFFER (object, current_buffer);
1163 i = validate_interval_range (object, &start, &end, hard);
1164 if (NULL_INTERVAL_P (i))
1165 return Qnil;
1167 s = XINT (start);
1168 len = XINT (end) - s;
1170 /* No need to protect OBJECT, because we GC only if it's a buffer,
1171 and live buffers are always protected. */
1172 GCPRO1 (properties);
1174 /* If we're not starting on an interval boundary, we have to
1175 split this interval. */
1176 if (i->position != s)
1178 /* If this interval already has the properties, we can
1179 skip it. */
1180 if (interval_has_all_properties (properties, i))
1182 int got = (LENGTH (i) - (s - i->position));
1183 if (got >= len)
1184 RETURN_UNGCPRO (Qnil);
1185 len -= got;
1186 i = next_interval (i);
1188 else
1190 unchanged = i;
1191 i = split_interval_right (unchanged, s - unchanged->position);
1192 copy_properties (unchanged, i);
1196 if (BUFFERP (object))
1197 modify_region (XBUFFER (object), XINT (start), XINT (end));
1199 /* We are at the beginning of interval I, with LEN chars to scan. */
1200 for (;;)
1202 if (i == 0)
1203 abort ();
1205 if (LENGTH (i) >= len)
1207 /* We can UNGCPRO safely here, because there will be just
1208 one more chance to gc, in the next call to add_properties,
1209 and after that we will not need PROPERTIES or OBJECT again. */
1210 UNGCPRO;
1212 if (interval_has_all_properties (properties, i))
1214 if (BUFFERP (object))
1215 signal_after_change (XINT (start), XINT (end) - XINT (start),
1216 XINT (end) - XINT (start));
1218 return modified ? Qt : Qnil;
1221 if (LENGTH (i) == len)
1223 add_properties (properties, i, object);
1224 if (BUFFERP (object))
1225 signal_after_change (XINT (start), XINT (end) - XINT (start),
1226 XINT (end) - XINT (start));
1227 return Qt;
1230 /* i doesn't have the properties, and goes past the change limit */
1231 unchanged = i;
1232 i = split_interval_left (unchanged, len);
1233 copy_properties (unchanged, i);
1234 add_properties (properties, i, object);
1235 if (BUFFERP (object))
1236 signal_after_change (XINT (start), XINT (end) - XINT (start),
1237 XINT (end) - XINT (start));
1238 return Qt;
1241 len -= LENGTH (i);
1242 modified += add_properties (properties, i, object);
1243 i = next_interval (i);
1247 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1249 DEFUN ("put-text-property", Fput_text_property,
1250 Sput_text_property, 4, 5, 0,
1251 doc: /* Set one property of the text from START to END.
1252 The third and fourth arguments PROPERTY and VALUE
1253 specify the property to add.
1254 The optional fifth argument, OBJECT,
1255 is the string or buffer containing the text. */)
1256 (start, end, property, value, object)
1257 Lisp_Object start, end, property, value, object;
1259 Fadd_text_properties (start, end,
1260 Fcons (property, Fcons (value, Qnil)),
1261 object);
1262 return Qnil;
1265 DEFUN ("set-text-properties", Fset_text_properties,
1266 Sset_text_properties, 3, 4, 0,
1267 doc: /* Completely replace properties of text from START to END.
1268 The third argument PROPERTIES is the new property list.
1269 The optional fourth argument, OBJECT,
1270 is the string or buffer containing the text.
1271 If OBJECT is omitted or nil, it defaults to the current buffer.
1272 If PROPERTIES is nil, the effect is to remove all properties from
1273 the designated part of OBJECT. */)
1274 (start, end, properties, object)
1275 Lisp_Object start, end, properties, object;
1277 return set_text_properties (start, end, properties, object, Qt);
1281 /* Replace properties of text from START to END with new list of
1282 properties PROPERTIES. OBJECT is the buffer or string containing
1283 the text. OBJECT nil means use the current buffer.
1284 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1285 is non-nil if properties were replaced; it is nil if there weren't
1286 any properties to replace. */
1288 Lisp_Object
1289 set_text_properties (start, end, properties, object, signal_after_change_p)
1290 Lisp_Object start, end, properties, object, signal_after_change_p;
1292 register INTERVAL i;
1293 Lisp_Object ostart, oend;
1295 ostart = start;
1296 oend = end;
1298 properties = validate_plist (properties);
1300 if (NILP (object))
1301 XSETBUFFER (object, current_buffer);
1303 /* If we want no properties for a whole string,
1304 get rid of its intervals. */
1305 if (NILP (properties) && STRINGP (object)
1306 && XFASTINT (start) == 0
1307 && XFASTINT (end) == SCHARS (object))
1309 if (! STRING_INTERVALS (object))
1310 return Qt;
1312 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1313 return Qt;
1316 i = validate_interval_range (object, &start, &end, soft);
1318 if (NULL_INTERVAL_P (i))
1320 /* If buffer has no properties, and we want none, return now. */
1321 if (NILP (properties))
1322 return Qnil;
1324 /* Restore the original START and END values
1325 because validate_interval_range increments them for strings. */
1326 start = ostart;
1327 end = oend;
1329 i = validate_interval_range (object, &start, &end, hard);
1330 /* This can return if start == end. */
1331 if (NULL_INTERVAL_P (i))
1332 return Qnil;
1335 if (BUFFERP (object))
1336 modify_region (XBUFFER (object), XINT (start), XINT (end));
1338 set_text_properties_1 (start, end, properties, object, i);
1340 if (BUFFERP (object) && !NILP (signal_after_change_p))
1341 signal_after_change (XINT (start), XINT (end) - XINT (start),
1342 XINT (end) - XINT (start));
1343 return Qt;
1346 /* Replace properties of text from START to END with new list of
1347 properties PROPERTIES. BUFFER is the buffer containing
1348 the text. This does not obey any hooks.
1349 You can provide the interval that START is located in as I,
1350 or pass NULL for I and this function will find it.
1351 START and END can be in any order. */
1353 void
1354 set_text_properties_1 (start, end, properties, buffer, i)
1355 Lisp_Object start, end, properties, buffer;
1356 INTERVAL i;
1358 register INTERVAL prev_changed = NULL_INTERVAL;
1359 register int s, len;
1360 INTERVAL unchanged;
1362 s = XINT (start);
1363 len = XINT (end) - s;
1364 if (len == 0)
1365 return;
1366 if (len < 0)
1368 s = s + len;
1369 len = - len;
1372 if (i == 0)
1373 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1375 if (i->position != s)
1377 unchanged = i;
1378 i = split_interval_right (unchanged, s - unchanged->position);
1380 if (LENGTH (i) > len)
1382 copy_properties (unchanged, i);
1383 i = split_interval_left (i, len);
1384 set_properties (properties, i, buffer);
1385 return;
1388 set_properties (properties, i, buffer);
1390 if (LENGTH (i) == len)
1391 return;
1393 prev_changed = i;
1394 len -= LENGTH (i);
1395 i = next_interval (i);
1398 /* We are starting at the beginning of an interval, I */
1399 while (len > 0)
1401 if (i == 0)
1402 abort ();
1404 if (LENGTH (i) >= len)
1406 if (LENGTH (i) > len)
1407 i = split_interval_left (i, len);
1409 /* We have to call set_properties even if we are going to
1410 merge the intervals, so as to make the undo records
1411 and cause redisplay to happen. */
1412 set_properties (properties, i, buffer);
1413 if (!NULL_INTERVAL_P (prev_changed))
1414 merge_interval_left (i);
1415 return;
1418 len -= LENGTH (i);
1420 /* We have to call set_properties even if we are going to
1421 merge the intervals, so as to make the undo records
1422 and cause redisplay to happen. */
1423 set_properties (properties, i, buffer);
1424 if (NULL_INTERVAL_P (prev_changed))
1425 prev_changed = i;
1426 else
1427 prev_changed = i = merge_interval_left (i);
1429 i = next_interval (i);
1433 DEFUN ("remove-text-properties", Fremove_text_properties,
1434 Sremove_text_properties, 3, 4, 0,
1435 doc: /* Remove some properties from text from START to END.
1436 The third argument PROPERTIES is a property list
1437 whose property names specify the properties to remove.
1438 \(The values stored in PROPERTIES are ignored.)
1439 The optional fourth argument, OBJECT,
1440 is the string or buffer containing the text.
1441 Return t if any property was actually removed, nil otherwise. */)
1442 (start, end, properties, object)
1443 Lisp_Object start, end, properties, object;
1445 register INTERVAL i, unchanged;
1446 register int s, len, modified = 0;
1448 if (NILP (object))
1449 XSETBUFFER (object, current_buffer);
1451 i = validate_interval_range (object, &start, &end, soft);
1452 if (NULL_INTERVAL_P (i))
1453 return Qnil;
1455 s = XINT (start);
1456 len = XINT (end) - s;
1458 if (i->position != s)
1460 /* No properties on this first interval -- return if
1461 it covers the entire region. */
1462 if (! interval_has_some_properties (properties, i))
1464 int got = (LENGTH (i) - (s - i->position));
1465 if (got >= len)
1466 return Qnil;
1467 len -= got;
1468 i = next_interval (i);
1470 /* Split away the beginning of this interval; what we don't
1471 want to modify. */
1472 else
1474 unchanged = i;
1475 i = split_interval_right (unchanged, s - unchanged->position);
1476 copy_properties (unchanged, i);
1480 if (BUFFERP (object))
1481 modify_region (XBUFFER (object), XINT (start), XINT (end));
1483 /* We are at the beginning of an interval, with len to scan */
1484 for (;;)
1486 if (i == 0)
1487 abort ();
1489 if (LENGTH (i) >= len)
1491 if (! interval_has_some_properties (properties, i))
1492 return modified ? Qt : Qnil;
1494 if (LENGTH (i) == len)
1496 remove_properties (properties, Qnil, i, object);
1497 if (BUFFERP (object))
1498 signal_after_change (XINT (start), XINT (end) - XINT (start),
1499 XINT (end) - XINT (start));
1500 return Qt;
1503 /* i has the properties, and goes past the change limit */
1504 unchanged = i;
1505 i = split_interval_left (i, len);
1506 copy_properties (unchanged, i);
1507 remove_properties (properties, Qnil, i, object);
1508 if (BUFFERP (object))
1509 signal_after_change (XINT (start), XINT (end) - XINT (start),
1510 XINT (end) - XINT (start));
1511 return Qt;
1514 len -= LENGTH (i);
1515 modified += remove_properties (properties, Qnil, i, object);
1516 i = next_interval (i);
1520 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1521 Sremove_list_of_text_properties, 3, 4, 0,
1522 doc: /* Remove some properties from text from START to END.
1523 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1524 The optional fourth argument, OBJECT,
1525 is the string or buffer containing the text, defaulting to the current buffer.
1526 Return t if any property was actually removed, nil otherwise. */)
1527 (start, end, list_of_properties, object)
1528 Lisp_Object start, end, list_of_properties, object;
1530 register INTERVAL i, unchanged;
1531 register int s, len, modified = 0;
1532 Lisp_Object properties;
1533 properties = list_of_properties;
1535 if (NILP (object))
1536 XSETBUFFER (object, current_buffer);
1538 i = validate_interval_range (object, &start, &end, soft);
1539 if (NULL_INTERVAL_P (i))
1540 return Qnil;
1542 s = XINT (start);
1543 len = XINT (end) - s;
1545 if (i->position != s)
1547 /* No properties on this first interval -- return if
1548 it covers the entire region. */
1549 if (! interval_has_some_properties_list (properties, i))
1551 int got = (LENGTH (i) - (s - i->position));
1552 if (got >= len)
1553 return Qnil;
1554 len -= got;
1555 i = next_interval (i);
1557 /* Split away the beginning of this interval; what we don't
1558 want to modify. */
1559 else
1561 unchanged = i;
1562 i = split_interval_right (unchanged, s - unchanged->position);
1563 copy_properties (unchanged, i);
1567 if (BUFFERP (object))
1568 modify_region (XBUFFER (object), XINT (start), XINT (end));
1570 /* We are at the beginning of an interval, with len to scan */
1571 for (;;)
1573 if (i == 0)
1574 abort ();
1576 if (LENGTH (i) >= len)
1578 if (! interval_has_some_properties_list (properties, i))
1579 return modified ? Qt : Qnil;
1581 if (LENGTH (i) == len)
1583 remove_properties (Qnil, properties, i, object);
1584 if (BUFFERP (object))
1585 signal_after_change (XINT (start), XINT (end) - XINT (start),
1586 XINT (end) - XINT (start));
1587 return Qt;
1590 /* i has the properties, and goes past the change limit */
1591 unchanged = i;
1592 i = split_interval_left (i, len);
1593 copy_properties (unchanged, i);
1594 remove_properties (Qnil, properties, i, object);
1595 if (BUFFERP (object))
1596 signal_after_change (XINT (start), XINT (end) - XINT (start),
1597 XINT (end) - XINT (start));
1598 return Qt;
1601 len -= LENGTH (i);
1602 modified += remove_properties (Qnil, properties, i, object);
1603 i = next_interval (i);
1607 DEFUN ("text-property-any", Ftext_property_any,
1608 Stext_property_any, 4, 5, 0,
1609 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1610 If so, return the position of the first character whose property PROPERTY
1611 is `eq' to VALUE. Otherwise return nil.
1612 The optional fifth argument, OBJECT, is the string or buffer
1613 containing the text. */)
1614 (start, end, property, value, object)
1615 Lisp_Object start, end, property, value, object;
1617 register INTERVAL i;
1618 register int e, pos;
1620 if (NILP (object))
1621 XSETBUFFER (object, current_buffer);
1622 i = validate_interval_range (object, &start, &end, soft);
1623 if (NULL_INTERVAL_P (i))
1624 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1625 e = XINT (end);
1627 while (! NULL_INTERVAL_P (i))
1629 if (i->position >= e)
1630 break;
1631 if (EQ (textget (i->plist, property), value))
1633 pos = i->position;
1634 if (pos < XINT (start))
1635 pos = XINT (start);
1636 return make_number (pos);
1638 i = next_interval (i);
1640 return Qnil;
1643 DEFUN ("text-property-not-all", Ftext_property_not_all,
1644 Stext_property_not_all, 4, 5, 0,
1645 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1646 If so, return the position of the first character whose property PROPERTY
1647 is not `eq' to VALUE. Otherwise, return nil.
1648 The optional fifth argument, OBJECT, is the string or buffer
1649 containing the text. */)
1650 (start, end, property, value, object)
1651 Lisp_Object start, end, property, value, object;
1653 register INTERVAL i;
1654 register int s, e;
1656 if (NILP (object))
1657 XSETBUFFER (object, current_buffer);
1658 i = validate_interval_range (object, &start, &end, soft);
1659 if (NULL_INTERVAL_P (i))
1660 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1661 s = XINT (start);
1662 e = XINT (end);
1664 while (! NULL_INTERVAL_P (i))
1666 if (i->position >= e)
1667 break;
1668 if (! EQ (textget (i->plist, property), value))
1670 if (i->position > s)
1671 s = i->position;
1672 return make_number (s);
1674 i = next_interval (i);
1676 return Qnil;
1680 /* Return the direction from which the text-property PROP would be
1681 inherited by any new text inserted at POS: 1 if it would be
1682 inherited from the char after POS, -1 if it would be inherited from
1683 the char before POS, and 0 if from neither. */
1686 text_property_stickiness (prop, pos)
1687 Lisp_Object prop;
1688 Lisp_Object pos;
1690 Lisp_Object prev_pos, front_sticky;
1691 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1693 if (XINT (pos) > BEGV)
1694 /* Consider previous character. */
1696 Lisp_Object rear_non_sticky;
1698 prev_pos = make_number (XINT (pos) - 1);
1699 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, Qnil);
1701 if (!NILP (CONSP (rear_non_sticky)
1702 ? Fmemq (prop, rear_non_sticky)
1703 : rear_non_sticky))
1704 /* PROP is rear-non-sticky. */
1705 is_rear_sticky = 0;
1708 /* Consider following character. */
1709 front_sticky = Fget_text_property (pos, Qfront_sticky, Qnil);
1711 if (EQ (front_sticky, Qt)
1712 || (CONSP (front_sticky)
1713 && !NILP (Fmemq (prop, front_sticky))))
1714 /* PROP is inherited from after. */
1715 is_front_sticky = 1;
1717 /* Simple cases, where the properties are consistent. */
1718 if (is_rear_sticky && !is_front_sticky)
1719 return -1;
1720 else if (!is_rear_sticky && is_front_sticky)
1721 return 1;
1722 else if (!is_rear_sticky && !is_front_sticky)
1723 return 0;
1725 /* The stickiness properties are inconsistent, so we have to
1726 disambiguate. Basically, rear-sticky wins, _except_ if the
1727 property that would be inherited has a value of nil, in which case
1728 front-sticky wins. */
1729 if (XINT (pos) == BEGV || NILP (Fget_text_property (prev_pos, prop, Qnil)))
1730 return 1;
1731 else
1732 return -1;
1736 /* I don't think this is the right interface to export; how often do you
1737 want to do something like this, other than when you're copying objects
1738 around?
1740 I think it would be better to have a pair of functions, one which
1741 returns the text properties of a region as a list of ranges and
1742 plists, and another which applies such a list to another object. */
1744 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1745 SRC and DEST may each refer to strings or buffers.
1746 Optional sixth argument PROP causes only that property to be copied.
1747 Properties are copied to DEST as if by `add-text-properties'.
1748 Return t if any property value actually changed, nil otherwise. */
1750 /* Note this can GC when DEST is a buffer. */
1752 Lisp_Object
1753 copy_text_properties (start, end, src, pos, dest, prop)
1754 Lisp_Object start, end, src, pos, dest, prop;
1756 INTERVAL i;
1757 Lisp_Object res;
1758 Lisp_Object stuff;
1759 Lisp_Object plist;
1760 int s, e, e2, p, len, modified = 0;
1761 struct gcpro gcpro1, gcpro2;
1763 i = validate_interval_range (src, &start, &end, soft);
1764 if (NULL_INTERVAL_P (i))
1765 return Qnil;
1767 CHECK_NUMBER_COERCE_MARKER (pos);
1769 Lisp_Object dest_start, dest_end;
1771 dest_start = pos;
1772 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1773 /* Apply this to a copy of pos; it will try to increment its arguments,
1774 which we don't want. */
1775 validate_interval_range (dest, &dest_start, &dest_end, soft);
1778 s = XINT (start);
1779 e = XINT (end);
1780 p = XINT (pos);
1782 stuff = Qnil;
1784 while (s < e)
1786 e2 = i->position + LENGTH (i);
1787 if (e2 > e)
1788 e2 = e;
1789 len = e2 - s;
1791 plist = i->plist;
1792 if (! NILP (prop))
1793 while (! NILP (plist))
1795 if (EQ (Fcar (plist), prop))
1797 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1798 break;
1800 plist = Fcdr (Fcdr (plist));
1802 if (! NILP (plist))
1804 /* Must defer modifications to the interval tree in case src
1805 and dest refer to the same string or buffer. */
1806 stuff = Fcons (Fcons (make_number (p),
1807 Fcons (make_number (p + len),
1808 Fcons (plist, Qnil))),
1809 stuff);
1812 i = next_interval (i);
1813 if (NULL_INTERVAL_P (i))
1814 break;
1816 p += len;
1817 s = i->position;
1820 GCPRO2 (stuff, dest);
1822 while (! NILP (stuff))
1824 res = Fcar (stuff);
1825 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1826 Fcar (Fcdr (Fcdr (res))), dest);
1827 if (! NILP (res))
1828 modified++;
1829 stuff = Fcdr (stuff);
1832 UNGCPRO;
1834 return modified ? Qt : Qnil;
1838 /* Return a list representing the text properties of OBJECT between
1839 START and END. if PROP is non-nil, report only on that property.
1840 Each result list element has the form (S E PLIST), where S and E
1841 are positions in OBJECT and PLIST is a property list containing the
1842 text properties of OBJECT between S and E. Value is nil if OBJECT
1843 doesn't contain text properties between START and END. */
1845 Lisp_Object
1846 text_property_list (object, start, end, prop)
1847 Lisp_Object object, start, end, prop;
1849 struct interval *i;
1850 Lisp_Object result;
1852 result = Qnil;
1854 i = validate_interval_range (object, &start, &end, soft);
1855 if (!NULL_INTERVAL_P (i))
1857 int s = XINT (start);
1858 int e = XINT (end);
1860 while (s < e)
1862 int interval_end, len;
1863 Lisp_Object plist;
1865 interval_end = i->position + LENGTH (i);
1866 if (interval_end > e)
1867 interval_end = e;
1868 len = interval_end - s;
1870 plist = i->plist;
1872 if (!NILP (prop))
1873 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1874 if (EQ (Fcar (plist), prop))
1876 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1877 break;
1880 if (!NILP (plist))
1881 result = Fcons (Fcons (make_number (s),
1882 Fcons (make_number (s + len),
1883 Fcons (plist, Qnil))),
1884 result);
1886 i = next_interval (i);
1887 if (NULL_INTERVAL_P (i))
1888 break;
1889 s = i->position;
1893 return result;
1897 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1898 (START END PLIST), where START and END are positions and PLIST is a
1899 property list containing the text properties to add. Adjust START
1900 and END positions by DELTA before adding properties. Value is
1901 non-zero if OBJECT was modified. */
1904 add_text_properties_from_list (object, list, delta)
1905 Lisp_Object object, list, delta;
1907 struct gcpro gcpro1, gcpro2;
1908 int modified_p = 0;
1910 GCPRO2 (list, object);
1912 for (; CONSP (list); list = XCDR (list))
1914 Lisp_Object item, start, end, plist, tem;
1916 item = XCAR (list);
1917 start = make_number (XINT (XCAR (item)) + XINT (delta));
1918 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1919 plist = XCAR (XCDR (XCDR (item)));
1921 tem = Fadd_text_properties (start, end, plist, object);
1922 if (!NILP (tem))
1923 modified_p = 1;
1926 UNGCPRO;
1927 return modified_p;
1932 /* Modify end-points of ranges in LIST destructively. LIST is a list
1933 as returned from text_property_list. Change end-points equal to
1934 OLD_END to NEW_END. */
1936 void
1937 extend_property_ranges (list, old_end, new_end)
1938 Lisp_Object list, old_end, new_end;
1940 for (; CONSP (list); list = XCDR (list))
1942 Lisp_Object item, end;
1944 item = XCAR (list);
1945 end = XCAR (XCDR (item));
1947 if (EQ (end, old_end))
1948 XSETCAR (XCDR (item), new_end);
1954 /* Call the modification hook functions in LIST, each with START and END. */
1956 static void
1957 call_mod_hooks (list, start, end)
1958 Lisp_Object list, start, end;
1960 struct gcpro gcpro1;
1961 GCPRO1 (list);
1962 while (!NILP (list))
1964 call2 (Fcar (list), start, end);
1965 list = Fcdr (list);
1967 UNGCPRO;
1970 /* Check for read-only intervals between character positions START ... END,
1971 in BUF, and signal an error if we find one.
1973 Then check for any modification hooks in the range.
1974 Create a list of all these hooks in lexicographic order,
1975 eliminating consecutive extra copies of the same hook. Then call
1976 those hooks in order, with START and END - 1 as arguments. */
1978 void
1979 verify_interval_modification (buf, start, end)
1980 struct buffer *buf;
1981 int start, end;
1983 register INTERVAL intervals = BUF_INTERVALS (buf);
1984 register INTERVAL i;
1985 Lisp_Object hooks;
1986 register Lisp_Object prev_mod_hooks;
1987 Lisp_Object mod_hooks;
1988 struct gcpro gcpro1;
1990 hooks = Qnil;
1991 prev_mod_hooks = Qnil;
1992 mod_hooks = Qnil;
1994 interval_insert_behind_hooks = Qnil;
1995 interval_insert_in_front_hooks = Qnil;
1997 if (NULL_INTERVAL_P (intervals))
1998 return;
2000 if (start > end)
2002 int temp = start;
2003 start = end;
2004 end = temp;
2007 /* For an insert operation, check the two chars around the position. */
2008 if (start == end)
2010 INTERVAL prev = NULL;
2011 Lisp_Object before, after;
2013 /* Set I to the interval containing the char after START,
2014 and PREV to the interval containing the char before START.
2015 Either one may be null. They may be equal. */
2016 i = find_interval (intervals, start);
2018 if (start == BUF_BEGV (buf))
2019 prev = 0;
2020 else if (i->position == start)
2021 prev = previous_interval (i);
2022 else if (i->position < start)
2023 prev = i;
2024 if (start == BUF_ZV (buf))
2025 i = 0;
2027 /* If Vinhibit_read_only is set and is not a list, we can
2028 skip the read_only checks. */
2029 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2031 /* If I and PREV differ we need to check for the read-only
2032 property together with its stickiness. If either I or
2033 PREV are 0, this check is all we need.
2034 We have to take special care, since read-only may be
2035 indirectly defined via the category property. */
2036 if (i != prev)
2038 if (! NULL_INTERVAL_P (i))
2040 after = textget (i->plist, Qread_only);
2042 /* If interval I is read-only and read-only is
2043 front-sticky, inhibit insertion.
2044 Check for read-only as well as category. */
2045 if (! NILP (after)
2046 && NILP (Fmemq (after, Vinhibit_read_only)))
2048 Lisp_Object tem;
2050 tem = textget (i->plist, Qfront_sticky);
2051 if (TMEM (Qread_only, tem)
2052 || (NILP (Fplist_get (i->plist, Qread_only))
2053 && TMEM (Qcategory, tem)))
2054 text_read_only (after);
2058 if (! NULL_INTERVAL_P (prev))
2060 before = textget (prev->plist, Qread_only);
2062 /* If interval PREV is read-only and read-only isn't
2063 rear-nonsticky, inhibit insertion.
2064 Check for read-only as well as category. */
2065 if (! NILP (before)
2066 && NILP (Fmemq (before, Vinhibit_read_only)))
2068 Lisp_Object tem;
2070 tem = textget (prev->plist, Qrear_nonsticky);
2071 if (! TMEM (Qread_only, tem)
2072 && (! NILP (Fplist_get (prev->plist,Qread_only))
2073 || ! TMEM (Qcategory, tem)))
2074 text_read_only (before);
2078 else if (! NULL_INTERVAL_P (i))
2080 after = textget (i->plist, Qread_only);
2082 /* If interval I is read-only and read-only is
2083 front-sticky, inhibit insertion.
2084 Check for read-only as well as category. */
2085 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2087 Lisp_Object tem;
2089 tem = textget (i->plist, Qfront_sticky);
2090 if (TMEM (Qread_only, tem)
2091 || (NILP (Fplist_get (i->plist, Qread_only))
2092 && TMEM (Qcategory, tem)))
2093 text_read_only (after);
2095 tem = textget (prev->plist, Qrear_nonsticky);
2096 if (! TMEM (Qread_only, tem)
2097 && (! NILP (Fplist_get (prev->plist, Qread_only))
2098 || ! TMEM (Qcategory, tem)))
2099 text_read_only (after);
2104 /* Run both insert hooks (just once if they're the same). */
2105 if (!NULL_INTERVAL_P (prev))
2106 interval_insert_behind_hooks
2107 = textget (prev->plist, Qinsert_behind_hooks);
2108 if (!NULL_INTERVAL_P (i))
2109 interval_insert_in_front_hooks
2110 = textget (i->plist, Qinsert_in_front_hooks);
2112 else
2114 /* Loop over intervals on or next to START...END,
2115 collecting their hooks. */
2117 i = find_interval (intervals, start);
2120 if (! INTERVAL_WRITABLE_P (i))
2121 text_read_only (textget (i->plist, Qread_only));
2123 if (!inhibit_modification_hooks)
2125 mod_hooks = textget (i->plist, Qmodification_hooks);
2126 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2128 hooks = Fcons (mod_hooks, hooks);
2129 prev_mod_hooks = mod_hooks;
2133 i = next_interval (i);
2135 /* Keep going thru the interval containing the char before END. */
2136 while (! NULL_INTERVAL_P (i) && i->position < end);
2138 if (!inhibit_modification_hooks)
2140 GCPRO1 (hooks);
2141 hooks = Fnreverse (hooks);
2142 while (! EQ (hooks, Qnil))
2144 call_mod_hooks (Fcar (hooks), make_number (start),
2145 make_number (end));
2146 hooks = Fcdr (hooks);
2148 UNGCPRO;
2153 /* Run the interval hooks for an insertion on character range START ... END.
2154 verify_interval_modification chose which hooks to run;
2155 this function is called after the insertion happens
2156 so it can indicate the range of inserted text. */
2158 void
2159 report_interval_modification (start, end)
2160 Lisp_Object start, end;
2162 if (! NILP (interval_insert_behind_hooks))
2163 call_mod_hooks (interval_insert_behind_hooks, start, end);
2164 if (! NILP (interval_insert_in_front_hooks)
2165 && ! EQ (interval_insert_in_front_hooks,
2166 interval_insert_behind_hooks))
2167 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2170 void
2171 syms_of_textprop ()
2173 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2174 doc: /* Property-list used as default values.
2175 The value of a property in this list is seen as the value for every
2176 character that does not have its own value for that property. */);
2177 Vdefault_text_properties = Qnil;
2179 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2180 doc: /* Alist of alternative properties for properties without a value.
2181 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2182 If a piece of text has no direct value for a particular property, then
2183 this alist is consulted. If that property appears in the alist, then
2184 the first non-nil value from the associated alternative properties is
2185 returned. */);
2186 Vchar_property_alias_alist = Qnil;
2188 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2189 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2190 This also inhibits the use of the `intangible' text property. */);
2191 Vinhibit_point_motion_hooks = Qnil;
2193 DEFVAR_LISP ("text-property-default-nonsticky",
2194 &Vtext_property_default_nonsticky,
2195 doc: /* Alist of properties vs the corresponding non-stickinesses.
2196 Each element has the form (PROPERTY . NONSTICKINESS).
2198 If a character in a buffer has PROPERTY, new text inserted adjacent to
2199 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2200 inherits it if NONSTICKINESS is nil. The front-sticky and
2201 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2202 Vtext_property_default_nonsticky = Qnil;
2204 staticpro (&interval_insert_behind_hooks);
2205 staticpro (&interval_insert_in_front_hooks);
2206 interval_insert_behind_hooks = Qnil;
2207 interval_insert_in_front_hooks = Qnil;
2210 /* Common attributes one might give text */
2212 staticpro (&Qforeground);
2213 Qforeground = intern ("foreground");
2214 staticpro (&Qbackground);
2215 Qbackground = intern ("background");
2216 staticpro (&Qfont);
2217 Qfont = intern ("font");
2218 staticpro (&Qstipple);
2219 Qstipple = intern ("stipple");
2220 staticpro (&Qunderline);
2221 Qunderline = intern ("underline");
2222 staticpro (&Qread_only);
2223 Qread_only = intern ("read-only");
2224 staticpro (&Qinvisible);
2225 Qinvisible = intern ("invisible");
2226 staticpro (&Qintangible);
2227 Qintangible = intern ("intangible");
2228 staticpro (&Qcategory);
2229 Qcategory = intern ("category");
2230 staticpro (&Qlocal_map);
2231 Qlocal_map = intern ("local-map");
2232 staticpro (&Qfront_sticky);
2233 Qfront_sticky = intern ("front-sticky");
2234 staticpro (&Qrear_nonsticky);
2235 Qrear_nonsticky = intern ("rear-nonsticky");
2236 staticpro (&Qmouse_face);
2237 Qmouse_face = intern ("mouse-face");
2239 /* Properties that text might use to specify certain actions */
2241 staticpro (&Qmouse_left);
2242 Qmouse_left = intern ("mouse-left");
2243 staticpro (&Qmouse_entered);
2244 Qmouse_entered = intern ("mouse-entered");
2245 staticpro (&Qpoint_left);
2246 Qpoint_left = intern ("point-left");
2247 staticpro (&Qpoint_entered);
2248 Qpoint_entered = intern ("point-entered");
2250 defsubr (&Stext_properties_at);
2251 defsubr (&Sget_text_property);
2252 defsubr (&Sget_char_property);
2253 defsubr (&Snext_char_property_change);
2254 defsubr (&Sprevious_char_property_change);
2255 defsubr (&Snext_single_char_property_change);
2256 defsubr (&Sprevious_single_char_property_change);
2257 defsubr (&Snext_property_change);
2258 defsubr (&Snext_single_property_change);
2259 defsubr (&Sprevious_property_change);
2260 defsubr (&Sprevious_single_property_change);
2261 defsubr (&Sadd_text_properties);
2262 defsubr (&Sput_text_property);
2263 defsubr (&Sset_text_properties);
2264 defsubr (&Sremove_text_properties);
2265 defsubr (&Sremove_list_of_text_properties);
2266 defsubr (&Stext_property_any);
2267 defsubr (&Stext_property_not_all);
2268 /* defsubr (&Serase_text_properties); */
2269 /* defsubr (&Scopy_text_properties); */