Remove * in defcustom docstrings.
[emacs.git] / src / textprop.c
blobfd70f039d226504ff065b899432480570104660a
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, 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;
81 static void text_read_only P_ ((Lisp_Object)) NO_RETURN;
84 /* Signal a `text-read-only' error. This function makes it easier
85 to capture that error in GDB by putting a breakpoint on it. */
87 static void
88 text_read_only (propval)
89 Lisp_Object propval;
91 if (STRINGP (propval))
92 xsignal1 (Qtext_read_only, propval);
94 xsignal0 (Qtext_read_only);
99 /* Extract the interval at the position pointed to by BEGIN from
100 OBJECT, a string or buffer. Additionally, check that the positions
101 pointed to by BEGIN and END are within the bounds of OBJECT, and
102 reverse them if *BEGIN is greater than *END. The objects pointed
103 to by BEGIN and END may be integers or markers; if the latter, they
104 are coerced to integers.
106 When OBJECT is a string, we increment *BEGIN and *END
107 to make them origin-one.
109 Note that buffer points don't correspond to interval indices.
110 For example, point-max is 1 greater than the index of the last
111 character. This difference is handled in the caller, which uses
112 the validated points to determine a length, and operates on that.
113 Exceptions are Ftext_properties_at, Fnext_property_change, and
114 Fprevious_property_change which call this function with BEGIN == END.
115 Handle this case specially.
117 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
118 create an interval tree for OBJECT if one doesn't exist, provided
119 the object actually contains text. In the current design, if there
120 is no text, there can be no text properties. */
122 #define soft 0
123 #define hard 1
125 INTERVAL
126 validate_interval_range (object, begin, end, force)
127 Lisp_Object object, *begin, *end;
128 int force;
130 register INTERVAL i;
131 int searchpos;
133 CHECK_STRING_OR_BUFFER (object);
134 CHECK_NUMBER_COERCE_MARKER (*begin);
135 CHECK_NUMBER_COERCE_MARKER (*end);
137 /* If we are asked for a point, but from a subr which operates
138 on a range, then return nothing. */
139 if (EQ (*begin, *end) && begin != end)
140 return NULL_INTERVAL;
142 if (XINT (*begin) > XINT (*end))
144 Lisp_Object n;
145 n = *begin;
146 *begin = *end;
147 *end = n;
150 if (BUFFERP (object))
152 register struct buffer *b = XBUFFER (object);
154 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
155 && XINT (*end) <= BUF_ZV (b)))
156 args_out_of_range (*begin, *end);
157 i = BUF_INTERVALS (b);
159 /* If there's no text, there are no properties. */
160 if (BUF_BEGV (b) == BUF_ZV (b))
161 return NULL_INTERVAL;
163 searchpos = XINT (*begin);
165 else
167 int len = SCHARS (object);
169 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
170 && XINT (*end) <= len))
171 args_out_of_range (*begin, *end);
172 XSETFASTINT (*begin, XFASTINT (*begin));
173 if (begin != end)
174 XSETFASTINT (*end, XFASTINT (*end));
175 i = STRING_INTERVALS (object);
177 if (len == 0)
178 return NULL_INTERVAL;
180 searchpos = XINT (*begin);
183 if (NULL_INTERVAL_P (i))
184 return (force ? create_root_interval (object) : i);
186 return find_interval (i, searchpos);
189 /* Validate LIST as a property list. If LIST is not a list, then
190 make one consisting of (LIST nil). Otherwise, verify that LIST
191 is even numbered and thus suitable as a plist. */
193 static Lisp_Object
194 validate_plist (list)
195 Lisp_Object list;
197 if (NILP (list))
198 return Qnil;
200 if (CONSP (list))
202 register int i;
203 register Lisp_Object tail;
204 for (i = 0, tail = list; !NILP (tail); i++)
206 tail = Fcdr (tail);
207 QUIT;
209 if (i & 1)
210 error ("Odd length text property list");
211 return list;
214 return Fcons (list, Fcons (Qnil, Qnil));
217 /* Return nonzero if interval I has all the properties,
218 with the same values, of list PLIST. */
220 static int
221 interval_has_all_properties (plist, i)
222 Lisp_Object plist;
223 INTERVAL i;
225 register Lisp_Object tail1, tail2, sym1;
226 register int found;
228 /* Go through each element of PLIST. */
229 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
231 sym1 = Fcar (tail1);
232 found = 0;
234 /* Go through I's plist, looking for sym1 */
235 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
236 if (EQ (sym1, Fcar (tail2)))
238 /* Found the same property on both lists. If the
239 values are unequal, return zero. */
240 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
241 return 0;
243 /* Property has same value on both lists; go to next one. */
244 found = 1;
245 break;
248 if (! found)
249 return 0;
252 return 1;
255 /* Return nonzero if the plist of interval I has any of the
256 properties of PLIST, regardless of their values. */
258 static INLINE int
259 interval_has_some_properties (plist, i)
260 Lisp_Object plist;
261 INTERVAL i;
263 register Lisp_Object tail1, tail2, sym;
265 /* Go through each element of PLIST. */
266 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
268 sym = Fcar (tail1);
270 /* Go through i's plist, looking for tail1 */
271 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
272 if (EQ (sym, Fcar (tail2)))
273 return 1;
276 return 0;
279 /* Return nonzero if the plist of interval I has any of the
280 property names in LIST, regardless of their values. */
282 static INLINE int
283 interval_has_some_properties_list (list, i)
284 Lisp_Object list;
285 INTERVAL i;
287 register Lisp_Object tail1, tail2, sym;
289 /* Go through each element of LIST. */
290 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
292 sym = Fcar (tail1);
294 /* Go through i's plist, looking for tail1 */
295 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
296 if (EQ (sym, XCAR (tail2)))
297 return 1;
300 return 0;
303 /* Changing the plists of individual intervals. */
305 /* Return the value of PROP in property-list PLIST, or Qunbound if it
306 has none. */
307 static Lisp_Object
308 property_value (plist, prop)
309 Lisp_Object plist, prop;
311 Lisp_Object value;
313 while (PLIST_ELT_P (plist, value))
314 if (EQ (XCAR (plist), prop))
315 return XCAR (value);
316 else
317 plist = XCDR (value);
319 return Qunbound;
322 /* Set the properties of INTERVAL to PROPERTIES,
323 and record undo info for the previous values.
324 OBJECT is the string or buffer that INTERVAL belongs to. */
326 static void
327 set_properties (properties, interval, object)
328 Lisp_Object properties, object;
329 INTERVAL interval;
331 Lisp_Object sym, value;
333 if (BUFFERP (object))
335 /* For each property in the old plist which is missing from PROPERTIES,
336 or has a different value in PROPERTIES, make an undo record. */
337 for (sym = interval->plist;
338 PLIST_ELT_P (sym, value);
339 sym = XCDR (value))
340 if (! EQ (property_value (properties, XCAR (sym)),
341 XCAR (value)))
343 record_property_change (interval->position, LENGTH (interval),
344 XCAR (sym), XCAR (value),
345 object);
348 /* For each new property that has no value at all in the old plist,
349 make an undo record binding it to nil, so it will be removed. */
350 for (sym = properties;
351 PLIST_ELT_P (sym, value);
352 sym = XCDR (value))
353 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
355 record_property_change (interval->position, LENGTH (interval),
356 XCAR (sym), Qnil,
357 object);
361 /* Store new properties. */
362 interval->plist = Fcopy_sequence (properties);
365 /* Add the properties of PLIST to the interval I, or set
366 the value of I's property to the value of the property on PLIST
367 if they are different.
369 OBJECT should be the string or buffer the interval is in.
371 Return nonzero if this changes I (i.e., if any members of PLIST
372 are actually added to I's plist) */
374 static int
375 add_properties (plist, i, object)
376 Lisp_Object plist;
377 INTERVAL i;
378 Lisp_Object object;
380 Lisp_Object tail1, tail2, sym1, val1;
381 register int changed = 0;
382 register int found;
383 struct gcpro gcpro1, gcpro2, gcpro3;
385 tail1 = plist;
386 sym1 = Qnil;
387 val1 = Qnil;
388 /* No need to protect OBJECT, because we can GC only in the case
389 where it is a buffer, and live buffers are always protected.
390 I and its plist are also protected, via OBJECT. */
391 GCPRO3 (tail1, sym1, val1);
393 /* Go through each element of PLIST. */
394 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
396 sym1 = Fcar (tail1);
397 val1 = Fcar (Fcdr (tail1));
398 found = 0;
400 /* Go through I's plist, looking for sym1 */
401 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
402 if (EQ (sym1, Fcar (tail2)))
404 /* No need to gcpro, because tail2 protects this
405 and it must be a cons cell (we get an error otherwise). */
406 register Lisp_Object this_cdr;
408 this_cdr = Fcdr (tail2);
409 /* Found the property. Now check its value. */
410 found = 1;
412 /* The properties have the same value on both lists.
413 Continue to the next property. */
414 if (EQ (val1, Fcar (this_cdr)))
415 break;
417 /* Record this change in the buffer, for undo purposes. */
418 if (BUFFERP (object))
420 record_property_change (i->position, LENGTH (i),
421 sym1, Fcar (this_cdr), object);
424 /* I's property has a different value -- change it */
425 Fsetcar (this_cdr, val1);
426 changed++;
427 break;
430 if (! found)
432 /* Record this change in the buffer, for undo purposes. */
433 if (BUFFERP (object))
435 record_property_change (i->position, LENGTH (i),
436 sym1, Qnil, object);
438 i->plist = Fcons (sym1, Fcons (val1, i->plist));
439 changed++;
443 UNGCPRO;
445 return changed;
448 /* For any members of PLIST, or LIST,
449 which are properties of I, remove them from I's plist.
450 (If PLIST is non-nil, use that, otherwise use LIST.)
451 OBJECT is the string or buffer containing I. */
453 static int
454 remove_properties (plist, list, i, object)
455 Lisp_Object plist, list;
456 INTERVAL i;
457 Lisp_Object object;
459 register Lisp_Object tail1, tail2, sym, current_plist;
460 register int changed = 0;
462 /* Nonzero means tail1 is a plist, otherwise it is a list. */
463 int use_plist;
465 current_plist = i->plist;
467 if (! NILP (plist))
468 tail1 = plist, use_plist = 1;
469 else
470 tail1 = list, use_plist = 0;
472 /* Go through each element of LIST or PLIST. */
473 while (CONSP (tail1))
475 sym = XCAR (tail1);
477 /* First, remove the symbol if it's at the head of the list */
478 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
480 if (BUFFERP (object))
481 record_property_change (i->position, LENGTH (i),
482 sym, XCAR (XCDR (current_plist)),
483 object);
485 current_plist = XCDR (XCDR (current_plist));
486 changed++;
489 /* Go through I's plist, looking for SYM. */
490 tail2 = current_plist;
491 while (! NILP (tail2))
493 register Lisp_Object this;
494 this = XCDR (XCDR (tail2));
495 if (CONSP (this) && EQ (sym, XCAR (this)))
497 if (BUFFERP (object))
498 record_property_change (i->position, LENGTH (i),
499 sym, XCAR (XCDR (this)), object);
501 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
502 changed++;
504 tail2 = this;
507 /* Advance thru TAIL1 one way or the other. */
508 tail1 = XCDR (tail1);
509 if (use_plist && CONSP (tail1))
510 tail1 = XCDR (tail1);
513 if (changed)
514 i->plist = current_plist;
515 return changed;
518 #if 0
519 /* Remove all properties from interval I. Return non-zero
520 if this changes the interval. */
522 static INLINE int
523 erase_properties (i)
524 INTERVAL i;
526 if (NILP (i->plist))
527 return 0;
529 i->plist = Qnil;
530 return 1;
532 #endif
534 /* Returns the interval of POSITION in OBJECT.
535 POSITION is BEG-based. */
537 INTERVAL
538 interval_of (position, object)
539 int position;
540 Lisp_Object object;
542 register INTERVAL i;
543 int beg, end;
545 if (NILP (object))
546 XSETBUFFER (object, current_buffer);
547 else if (EQ (object, Qt))
548 return NULL_INTERVAL;
550 CHECK_STRING_OR_BUFFER (object);
552 if (BUFFERP (object))
554 register struct buffer *b = XBUFFER (object);
556 beg = BUF_BEGV (b);
557 end = BUF_ZV (b);
558 i = BUF_INTERVALS (b);
560 else
562 beg = 0;
563 end = SCHARS (object);
564 i = STRING_INTERVALS (object);
567 if (!(beg <= position && position <= end))
568 args_out_of_range (make_number (position), make_number (position));
569 if (beg == end || NULL_INTERVAL_P (i))
570 return NULL_INTERVAL;
572 return find_interval (i, position);
575 DEFUN ("text-properties-at", Ftext_properties_at,
576 Stext_properties_at, 1, 2, 0,
577 doc: /* Return the list of properties of the character at POSITION in OBJECT.
578 If the optional second argument OBJECT is a buffer (or nil, which means
579 the current buffer), POSITION is a buffer position (integer or marker).
580 If OBJECT is a string, POSITION is a 0-based index into it.
581 If POSITION is at the end of OBJECT, the value is nil. */)
582 (position, object)
583 Lisp_Object position, object;
585 register INTERVAL i;
587 if (NILP (object))
588 XSETBUFFER (object, current_buffer);
590 i = validate_interval_range (object, &position, &position, soft);
591 if (NULL_INTERVAL_P (i))
592 return Qnil;
593 /* If POSITION is at the end of the interval,
594 it means it's the end of OBJECT.
595 There are no properties at the very end,
596 since no character follows. */
597 if (XINT (position) == LENGTH (i) + i->position)
598 return Qnil;
600 return i->plist;
603 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
604 doc: /* Return the value of POSITION's property PROP, in OBJECT.
605 OBJECT is optional and defaults to the current buffer.
606 If POSITION is at the end of OBJECT, the value is nil. */)
607 (position, prop, object)
608 Lisp_Object position, object;
609 Lisp_Object prop;
611 return textget (Ftext_properties_at (position, object), prop);
614 /* Return the value of char's property PROP, in OBJECT at POSITION.
615 OBJECT is optional and defaults to the current buffer.
616 If OVERLAY is non-0, then in the case that the returned property is from
617 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
618 returned in *OVERLAY.
619 If POSITION is at the end of OBJECT, the value is nil.
620 If OBJECT is a buffer, then overlay properties are considered as well as
621 text properties.
622 If OBJECT is a window, then that window's buffer is used, but
623 window-specific overlays are considered only if they are associated
624 with OBJECT. */
625 Lisp_Object
626 get_char_property_and_overlay (position, prop, object, overlay)
627 Lisp_Object position, object;
628 register Lisp_Object prop;
629 Lisp_Object *overlay;
631 struct window *w = 0;
633 CHECK_NUMBER_COERCE_MARKER (position);
635 if (NILP (object))
636 XSETBUFFER (object, current_buffer);
638 if (WINDOWP (object))
640 w = XWINDOW (object);
641 object = w->buffer;
643 if (BUFFERP (object))
645 int noverlays;
646 Lisp_Object *overlay_vec;
647 struct buffer *obuf = current_buffer;
649 set_buffer_temp (XBUFFER (object));
651 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
652 noverlays = sort_overlays (overlay_vec, noverlays, w);
654 set_buffer_temp (obuf);
656 /* Now check the overlays in order of decreasing priority. */
657 while (--noverlays >= 0)
659 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
660 if (!NILP (tem))
662 if (overlay)
663 /* Return the overlay we got the property from. */
664 *overlay = overlay_vec[noverlays];
665 return tem;
670 if (overlay)
671 /* Indicate that the return value is not from an overlay. */
672 *overlay = Qnil;
674 /* Not a buffer, or no appropriate overlay, so fall through to the
675 simpler case. */
676 return Fget_text_property (position, prop, object);
679 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
680 doc: /* Return the value of POSITION's property PROP, in OBJECT.
681 Both overlay properties and text properties are checked.
682 OBJECT is optional and defaults to the current buffer.
683 If POSITION is at the end of OBJECT, the value is nil.
684 If OBJECT is a buffer, then overlay properties are considered as well as
685 text properties.
686 If OBJECT is a window, then that window's buffer is used, but window-specific
687 overlays are considered only if they are associated with OBJECT. */)
688 (position, prop, object)
689 Lisp_Object position, object;
690 register Lisp_Object prop;
692 return get_char_property_and_overlay (position, prop, object, 0);
695 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
696 Sget_char_property_and_overlay, 2, 3, 0,
697 doc: /* Like `get-char-property', but with extra overlay information.
698 The value is a cons cell. Its car is the return value of `get-char-property'
699 with the same arguments--that is, the value of POSITION's property
700 PROP in OBJECT. Its cdr is the overlay in which the property was
701 found, or nil, if it was found as a text property or not found at all.
703 OBJECT is optional and defaults to the current buffer. OBJECT may be
704 a string, a buffer or a window. For strings, the cdr of the return
705 value is always nil, since strings do not have overlays. If OBJECT is
706 a window, then that window's buffer is used, but window-specific
707 overlays are considered only if they are associated with OBJECT. If
708 POSITION is at the end of OBJECT, both car and cdr are nil. */)
709 (position, prop, object)
710 Lisp_Object position, object;
711 register Lisp_Object prop;
713 Lisp_Object overlay;
714 Lisp_Object val
715 = get_char_property_and_overlay (position, prop, object, &overlay);
716 return Fcons(val, overlay);
720 DEFUN ("next-char-property-change", Fnext_char_property_change,
721 Snext_char_property_change, 1, 2, 0,
722 doc: /* Return the position of next text property or overlay change.
723 This scans characters forward in the current buffer from POSITION till
724 it finds a change in some text property, or the beginning or end of an
725 overlay, and returns the position of that.
726 If none is found up to (point-max), the function returns (point-max).
728 If the optional second argument LIMIT is non-nil, don't search
729 past position LIMIT; return LIMIT if nothing is found before LIMIT.
730 LIMIT is a no-op if it is greater than (point-max). */)
731 (position, limit)
732 Lisp_Object position, limit;
734 Lisp_Object temp;
736 temp = Fnext_overlay_change (position);
737 if (! NILP (limit))
739 CHECK_NUMBER_COERCE_MARKER (limit);
740 if (XINT (limit) < XINT (temp))
741 temp = limit;
743 return Fnext_property_change (position, Qnil, temp);
746 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
747 Sprevious_char_property_change, 1, 2, 0,
748 doc: /* Return the position of previous text property or overlay change.
749 Scans characters backward in the current buffer from POSITION till it
750 finds a change in some text property, or the beginning or end of an
751 overlay, and returns the position of that.
752 If none is found since (point-min), the function returns (point-min).
754 If the optional second argument LIMIT is non-nil, don't search
755 past position LIMIT; return LIMIT if nothing is found before LIMIT.
756 LIMIT is a no-op if it is less than (point-min). */)
757 (position, limit)
758 Lisp_Object position, limit;
760 Lisp_Object temp;
762 temp = Fprevious_overlay_change (position);
763 if (! NILP (limit))
765 CHECK_NUMBER_COERCE_MARKER (limit);
766 if (XINT (limit) > XINT (temp))
767 temp = limit;
769 return Fprevious_property_change (position, Qnil, temp);
773 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
774 Snext_single_char_property_change, 2, 4, 0,
775 doc: /* Return the position of next text property or overlay change for a specific property.
776 Scans characters forward from POSITION till it finds
777 a change in the PROP property, then returns the position of the change.
778 If the optional third argument OBJECT is a buffer (or nil, which means
779 the current buffer), POSITION is a buffer position (integer or marker).
780 If OBJECT is a string, POSITION is a 0-based index into it.
782 In a string, scan runs to the end of the string.
783 In a buffer, it runs to (point-max), and the value cannot exceed that.
785 The property values are compared with `eq'.
786 If the property is constant all the way to the end of OBJECT, return the
787 last valid position in OBJECT.
788 If the optional fourth argument LIMIT is non-nil, don't search
789 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
790 (position, prop, object, limit)
791 Lisp_Object prop, position, object, limit;
793 if (STRINGP (object))
795 position = Fnext_single_property_change (position, prop, object, limit);
796 if (NILP (position))
798 if (NILP (limit))
799 position = make_number (SCHARS (object));
800 else
802 CHECK_NUMBER (limit);
803 position = limit;
807 else
809 Lisp_Object initial_value, value;
810 int count = SPECPDL_INDEX ();
812 if (! NILP (object))
813 CHECK_BUFFER (object);
815 if (BUFFERP (object) && current_buffer != XBUFFER (object))
817 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
818 Fset_buffer (object);
821 CHECK_NUMBER_COERCE_MARKER (position);
823 initial_value = Fget_char_property (position, prop, object);
825 if (NILP (limit))
826 XSETFASTINT (limit, ZV);
827 else
828 CHECK_NUMBER_COERCE_MARKER (limit);
830 if (XFASTINT (position) >= XFASTINT (limit))
832 position = limit;
833 if (XFASTINT (position) > ZV)
834 XSETFASTINT (position, ZV);
836 else
837 while (1)
839 position = Fnext_char_property_change (position, limit);
840 if (XFASTINT (position) >= XFASTINT (limit))
842 position = limit;
843 break;
846 value = Fget_char_property (position, prop, object);
847 if (!EQ (value, initial_value))
848 break;
851 unbind_to (count, Qnil);
854 return position;
857 DEFUN ("previous-single-char-property-change",
858 Fprevious_single_char_property_change,
859 Sprevious_single_char_property_change, 2, 4, 0,
860 doc: /* Return the position of previous text property or overlay change for a specific property.
861 Scans characters backward from POSITION till it finds
862 a change in the PROP property, then returns the position of the change.
863 If the optional third argument OBJECT is a buffer (or nil, which means
864 the current buffer), POSITION is a buffer position (integer or marker).
865 If OBJECT is a string, POSITION is a 0-based index into it.
867 In a string, scan runs to the start of the string.
868 In a buffer, it runs to (point-min), and the value cannot be less than that.
870 The property values are compared with `eq'.
871 If the property is constant all the way to the start of OBJECT, return the
872 first valid position in OBJECT.
873 If the optional fourth argument LIMIT is non-nil, don't search
874 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
875 (position, prop, object, limit)
876 Lisp_Object prop, position, object, limit;
878 if (STRINGP (object))
880 position = Fprevious_single_property_change (position, prop, object, limit);
881 if (NILP (position))
883 if (NILP (limit))
884 position = make_number (SCHARS (object));
885 else
887 CHECK_NUMBER (limit);
888 position = limit;
892 else
894 int count = SPECPDL_INDEX ();
896 if (! NILP (object))
897 CHECK_BUFFER (object);
899 if (BUFFERP (object) && current_buffer != XBUFFER (object))
901 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
902 Fset_buffer (object);
905 CHECK_NUMBER_COERCE_MARKER (position);
907 if (NILP (limit))
908 XSETFASTINT (limit, BEGV);
909 else
910 CHECK_NUMBER_COERCE_MARKER (limit);
912 if (XFASTINT (position) <= XFASTINT (limit))
914 position = limit;
915 if (XFASTINT (position) < BEGV)
916 XSETFASTINT (position, BEGV);
918 else
920 Lisp_Object initial_value
921 = Fget_char_property (make_number (XFASTINT (position) - 1),
922 prop, object);
924 while (1)
926 position = Fprevious_char_property_change (position, limit);
928 if (XFASTINT (position) <= XFASTINT (limit))
930 position = limit;
931 break;
933 else
935 Lisp_Object value
936 = Fget_char_property (make_number (XFASTINT (position) - 1),
937 prop, object);
939 if (!EQ (value, initial_value))
940 break;
945 unbind_to (count, Qnil);
948 return position;
951 DEFUN ("next-property-change", Fnext_property_change,
952 Snext_property_change, 1, 3, 0,
953 doc: /* Return the position of next property change.
954 Scans characters forward from POSITION in OBJECT till it finds
955 a change in some text property, then returns the position of the change.
956 If the optional second argument OBJECT is a buffer (or nil, which means
957 the current buffer), POSITION is a buffer position (integer or marker).
958 If OBJECT is a string, POSITION is a 0-based index into it.
959 Return nil if the property is constant all the way to the end of OBJECT.
960 If the value is non-nil, it is a position greater than POSITION, never equal.
962 If the optional third argument LIMIT is non-nil, don't search
963 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
964 (position, object, limit)
965 Lisp_Object position, object, limit;
967 register INTERVAL i, next;
969 if (NILP (object))
970 XSETBUFFER (object, current_buffer);
972 if (!NILP (limit) && !EQ (limit, Qt))
973 CHECK_NUMBER_COERCE_MARKER (limit);
975 i = validate_interval_range (object, &position, &position, soft);
977 /* If LIMIT is t, return start of next interval--don't
978 bother checking further intervals. */
979 if (EQ (limit, Qt))
981 if (NULL_INTERVAL_P (i))
982 next = i;
983 else
984 next = next_interval (i);
986 if (NULL_INTERVAL_P (next))
987 XSETFASTINT (position, (STRINGP (object)
988 ? SCHARS (object)
989 : BUF_ZV (XBUFFER (object))));
990 else
991 XSETFASTINT (position, next->position);
992 return position;
995 if (NULL_INTERVAL_P (i))
996 return limit;
998 next = next_interval (i);
1000 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
1001 && (NILP (limit) || next->position < XFASTINT (limit)))
1002 next = next_interval (next);
1004 if (NULL_INTERVAL_P (next))
1005 return limit;
1006 if (NILP (limit))
1007 XSETFASTINT (limit, (STRINGP (object)
1008 ? SCHARS (object)
1009 : BUF_ZV (XBUFFER (object))));
1010 if (!(next->position < XFASTINT (limit)))
1011 return limit;
1013 XSETFASTINT (position, next->position);
1014 return position;
1017 /* Return 1 if there's a change in some property between BEG and END. */
1020 property_change_between_p (beg, end)
1021 int beg, end;
1023 register INTERVAL i, next;
1024 Lisp_Object object, pos;
1026 XSETBUFFER (object, current_buffer);
1027 XSETFASTINT (pos, beg);
1029 i = validate_interval_range (object, &pos, &pos, soft);
1030 if (NULL_INTERVAL_P (i))
1031 return 0;
1033 next = next_interval (i);
1034 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
1036 next = next_interval (next);
1037 if (NULL_INTERVAL_P (next))
1038 return 0;
1039 if (next->position >= end)
1040 return 0;
1043 if (NULL_INTERVAL_P (next))
1044 return 0;
1046 return 1;
1049 DEFUN ("next-single-property-change", Fnext_single_property_change,
1050 Snext_single_property_change, 2, 4, 0,
1051 doc: /* Return the position of next property change for a specific property.
1052 Scans characters forward from POSITION till it finds
1053 a change in the PROP property, then returns the position of the change.
1054 If the optional third argument OBJECT is a buffer (or nil, which means
1055 the current buffer), POSITION is a buffer position (integer or marker).
1056 If OBJECT is a string, POSITION is a 0-based index into it.
1057 The property values are compared with `eq'.
1058 Return nil if the property is constant all the way to the end of OBJECT.
1059 If the value is non-nil, it is a position greater than POSITION, never equal.
1061 If the optional fourth argument LIMIT is non-nil, don't search
1062 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1063 (position, prop, object, limit)
1064 Lisp_Object position, prop, object, limit;
1066 register INTERVAL i, next;
1067 register Lisp_Object here_val;
1069 if (NILP (object))
1070 XSETBUFFER (object, current_buffer);
1072 if (!NILP (limit))
1073 CHECK_NUMBER_COERCE_MARKER (limit);
1075 i = validate_interval_range (object, &position, &position, soft);
1076 if (NULL_INTERVAL_P (i))
1077 return limit;
1079 here_val = textget (i->plist, prop);
1080 next = next_interval (i);
1081 while (! NULL_INTERVAL_P (next)
1082 && EQ (here_val, textget (next->plist, prop))
1083 && (NILP (limit) || next->position < XFASTINT (limit)))
1084 next = next_interval (next);
1086 if (NULL_INTERVAL_P (next))
1087 return limit;
1088 if (NILP (limit))
1089 XSETFASTINT (limit, (STRINGP (object)
1090 ? SCHARS (object)
1091 : BUF_ZV (XBUFFER (object))));
1092 if (!(next->position < XFASTINT (limit)))
1093 return limit;
1095 return make_number (next->position);
1098 DEFUN ("previous-property-change", Fprevious_property_change,
1099 Sprevious_property_change, 1, 3, 0,
1100 doc: /* Return the position of previous property change.
1101 Scans characters backwards from POSITION in OBJECT till it finds
1102 a change in some text property, then returns the position of the change.
1103 If the optional second argument OBJECT is a buffer (or nil, which means
1104 the current buffer), POSITION is a buffer position (integer or marker).
1105 If OBJECT is a string, POSITION is a 0-based index into it.
1106 Return nil if the property is constant all the way to the start of OBJECT.
1107 If the value is non-nil, it is a position less than POSITION, never equal.
1109 If the optional third argument LIMIT is non-nil, don't search
1110 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1111 (position, object, limit)
1112 Lisp_Object position, object, limit;
1114 register INTERVAL i, previous;
1116 if (NILP (object))
1117 XSETBUFFER (object, current_buffer);
1119 if (!NILP (limit))
1120 CHECK_NUMBER_COERCE_MARKER (limit);
1122 i = validate_interval_range (object, &position, &position, soft);
1123 if (NULL_INTERVAL_P (i))
1124 return limit;
1126 /* Start with the interval containing the char before point. */
1127 if (i->position == XFASTINT (position))
1128 i = previous_interval (i);
1130 previous = previous_interval (i);
1131 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1132 && (NILP (limit)
1133 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1134 previous = previous_interval (previous);
1135 if (NULL_INTERVAL_P (previous))
1136 return limit;
1137 if (NILP (limit))
1138 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1139 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1140 return limit;
1142 return make_number (previous->position + LENGTH (previous));
1145 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1146 Sprevious_single_property_change, 2, 4, 0,
1147 doc: /* Return the position of previous property change for a specific property.
1148 Scans characters backward from POSITION till it finds
1149 a change in the PROP property, then returns the position of the change.
1150 If the optional third argument OBJECT is a buffer (or nil, which means
1151 the current buffer), POSITION is a buffer position (integer or marker).
1152 If OBJECT is a string, POSITION is a 0-based index into it.
1153 The property values are compared with `eq'.
1154 Return nil if the property is constant all the way to the start of OBJECT.
1155 If the value is non-nil, it is a position less than POSITION, never equal.
1157 If the optional fourth argument LIMIT is non-nil, don't search
1158 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1159 (position, prop, object, limit)
1160 Lisp_Object position, prop, object, limit;
1162 register INTERVAL i, previous;
1163 register Lisp_Object here_val;
1165 if (NILP (object))
1166 XSETBUFFER (object, current_buffer);
1168 if (!NILP (limit))
1169 CHECK_NUMBER_COERCE_MARKER (limit);
1171 i = validate_interval_range (object, &position, &position, soft);
1173 /* Start with the interval containing the char before point. */
1174 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1175 i = previous_interval (i);
1177 if (NULL_INTERVAL_P (i))
1178 return limit;
1180 here_val = textget (i->plist, prop);
1181 previous = previous_interval (i);
1182 while (!NULL_INTERVAL_P (previous)
1183 && EQ (here_val, textget (previous->plist, prop))
1184 && (NILP (limit)
1185 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1186 previous = previous_interval (previous);
1187 if (NULL_INTERVAL_P (previous))
1188 return limit;
1189 if (NILP (limit))
1190 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1191 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1192 return limit;
1194 return make_number (previous->position + LENGTH (previous));
1197 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1199 DEFUN ("add-text-properties", Fadd_text_properties,
1200 Sadd_text_properties, 3, 4, 0,
1201 doc: /* Add properties to the text from START to END.
1202 The third argument PROPERTIES is a property list
1203 specifying the property values to add. If the optional fourth argument
1204 OBJECT is a buffer (or nil, which means the current buffer),
1205 START and END are buffer positions (integers or markers).
1206 If OBJECT is a string, START and END are 0-based indices into it.
1207 Return t if any property value actually changed, nil otherwise. */)
1208 (start, end, properties, object)
1209 Lisp_Object start, end, properties, object;
1211 register INTERVAL i, unchanged;
1212 register int s, len, modified = 0;
1213 struct gcpro gcpro1;
1215 properties = validate_plist (properties);
1216 if (NILP (properties))
1217 return Qnil;
1219 if (NILP (object))
1220 XSETBUFFER (object, current_buffer);
1222 i = validate_interval_range (object, &start, &end, hard);
1223 if (NULL_INTERVAL_P (i))
1224 return Qnil;
1226 s = XINT (start);
1227 len = XINT (end) - s;
1229 /* No need to protect OBJECT, because we GC only if it's a buffer,
1230 and live buffers are always protected. */
1231 GCPRO1 (properties);
1233 /* If we're not starting on an interval boundary, we have to
1234 split this interval. */
1235 if (i->position != s)
1237 /* If this interval already has the properties, we can
1238 skip it. */
1239 if (interval_has_all_properties (properties, i))
1241 int got = (LENGTH (i) - (s - i->position));
1242 if (got >= len)
1243 RETURN_UNGCPRO (Qnil);
1244 len -= got;
1245 i = next_interval (i);
1247 else
1249 unchanged = i;
1250 i = split_interval_right (unchanged, s - unchanged->position);
1251 copy_properties (unchanged, i);
1255 if (BUFFERP (object))
1256 modify_region (XBUFFER (object), XINT (start), XINT (end));
1258 /* We are at the beginning of interval I, with LEN chars to scan. */
1259 for (;;)
1261 if (i == 0)
1262 abort ();
1264 if (LENGTH (i) >= len)
1266 /* We can UNGCPRO safely here, because there will be just
1267 one more chance to gc, in the next call to add_properties,
1268 and after that we will not need PROPERTIES or OBJECT again. */
1269 UNGCPRO;
1271 if (interval_has_all_properties (properties, i))
1273 if (BUFFERP (object))
1274 signal_after_change (XINT (start), XINT (end) - XINT (start),
1275 XINT (end) - XINT (start));
1277 return modified ? Qt : Qnil;
1280 if (LENGTH (i) == len)
1282 add_properties (properties, i, object);
1283 if (BUFFERP (object))
1284 signal_after_change (XINT (start), XINT (end) - XINT (start),
1285 XINT (end) - XINT (start));
1286 return Qt;
1289 /* i doesn't have the properties, and goes past the change limit */
1290 unchanged = i;
1291 i = split_interval_left (unchanged, len);
1292 copy_properties (unchanged, i);
1293 add_properties (properties, i, object);
1294 if (BUFFERP (object))
1295 signal_after_change (XINT (start), XINT (end) - XINT (start),
1296 XINT (end) - XINT (start));
1297 return Qt;
1300 len -= LENGTH (i);
1301 modified += add_properties (properties, i, object);
1302 i = next_interval (i);
1306 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1308 DEFUN ("put-text-property", Fput_text_property,
1309 Sput_text_property, 4, 5, 0,
1310 doc: /* Set one property of the text from START to END.
1311 The third and fourth arguments PROPERTY and VALUE
1312 specify the property to add.
1313 If the optional fifth argument OBJECT is a buffer (or nil, which means
1314 the current buffer), START and END are buffer positions (integers or
1315 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1316 (start, end, property, value, object)
1317 Lisp_Object start, end, property, value, object;
1319 Fadd_text_properties (start, end,
1320 Fcons (property, Fcons (value, Qnil)),
1321 object);
1322 return Qnil;
1325 DEFUN ("set-text-properties", Fset_text_properties,
1326 Sset_text_properties, 3, 4, 0,
1327 doc: /* Completely replace properties of text from START to END.
1328 The third argument PROPERTIES is the new property list.
1329 If the optional fourth argument OBJECT is a buffer (or nil, which means
1330 the current buffer), START and END are buffer positions (integers or
1331 markers). If OBJECT is a string, START and END are 0-based indices into it.
1332 If PROPERTIES is nil, the effect is to remove all properties from
1333 the designated part of OBJECT. */)
1334 (start, end, properties, object)
1335 Lisp_Object start, end, properties, object;
1337 return set_text_properties (start, end, properties, object, Qt);
1341 /* Replace properties of text from START to END with new list of
1342 properties PROPERTIES. OBJECT is the buffer or string containing
1343 the text. OBJECT nil means use the current buffer.
1344 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1345 is nil if the function _detected_ that it did not replace any
1346 properties, non-nil otherwise. */
1348 Lisp_Object
1349 set_text_properties (start, end, properties, object, signal_after_change_p)
1350 Lisp_Object start, end, properties, object, signal_after_change_p;
1352 register INTERVAL i;
1353 Lisp_Object ostart, oend;
1355 ostart = start;
1356 oend = end;
1358 properties = validate_plist (properties);
1360 if (NILP (object))
1361 XSETBUFFER (object, current_buffer);
1363 /* If we want no properties for a whole string,
1364 get rid of its intervals. */
1365 if (NILP (properties) && STRINGP (object)
1366 && XFASTINT (start) == 0
1367 && XFASTINT (end) == SCHARS (object))
1369 if (! STRING_INTERVALS (object))
1370 return Qnil;
1372 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1373 return Qt;
1376 i = validate_interval_range (object, &start, &end, soft);
1378 if (NULL_INTERVAL_P (i))
1380 /* If buffer has no properties, and we want none, return now. */
1381 if (NILP (properties))
1382 return Qnil;
1384 /* Restore the original START and END values
1385 because validate_interval_range increments them for strings. */
1386 start = ostart;
1387 end = oend;
1389 i = validate_interval_range (object, &start, &end, hard);
1390 /* This can return if start == end. */
1391 if (NULL_INTERVAL_P (i))
1392 return Qnil;
1395 if (BUFFERP (object))
1396 modify_region (XBUFFER (object), XINT (start), XINT (end));
1398 set_text_properties_1 (start, end, properties, object, i);
1400 if (BUFFERP (object) && !NILP (signal_after_change_p))
1401 signal_after_change (XINT (start), XINT (end) - XINT (start),
1402 XINT (end) - XINT (start));
1403 return Qt;
1406 /* Replace properties of text from START to END with new list of
1407 properties PROPERTIES. BUFFER is the buffer containing
1408 the text. This does not obey any hooks.
1409 You can provide the interval that START is located in as I,
1410 or pass NULL for I and this function will find it.
1411 START and END can be in any order. */
1413 void
1414 set_text_properties_1 (start, end, properties, buffer, i)
1415 Lisp_Object start, end, properties, buffer;
1416 INTERVAL i;
1418 register INTERVAL prev_changed = NULL_INTERVAL;
1419 register int s, len;
1420 INTERVAL unchanged;
1422 s = XINT (start);
1423 len = XINT (end) - s;
1424 if (len == 0)
1425 return;
1426 if (len < 0)
1428 s = s + len;
1429 len = - len;
1432 if (i == 0)
1433 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1435 if (i->position != s)
1437 unchanged = i;
1438 i = split_interval_right (unchanged, s - unchanged->position);
1440 if (LENGTH (i) > len)
1442 copy_properties (unchanged, i);
1443 i = split_interval_left (i, len);
1444 set_properties (properties, i, buffer);
1445 return;
1448 set_properties (properties, i, buffer);
1450 if (LENGTH (i) == len)
1451 return;
1453 prev_changed = i;
1454 len -= LENGTH (i);
1455 i = next_interval (i);
1458 /* We are starting at the beginning of an interval, I */
1459 while (len > 0)
1461 if (i == 0)
1462 abort ();
1464 if (LENGTH (i) >= len)
1466 if (LENGTH (i) > len)
1467 i = split_interval_left (i, len);
1469 /* We have to call set_properties even if we are going to
1470 merge the intervals, so as to make the undo records
1471 and cause redisplay to happen. */
1472 set_properties (properties, i, buffer);
1473 if (!NULL_INTERVAL_P (prev_changed))
1474 merge_interval_left (i);
1475 return;
1478 len -= LENGTH (i);
1480 /* We have to call set_properties even if we are going to
1481 merge the intervals, so as to make the undo records
1482 and cause redisplay to happen. */
1483 set_properties (properties, i, buffer);
1484 if (NULL_INTERVAL_P (prev_changed))
1485 prev_changed = i;
1486 else
1487 prev_changed = i = merge_interval_left (i);
1489 i = next_interval (i);
1493 DEFUN ("remove-text-properties", Fremove_text_properties,
1494 Sremove_text_properties, 3, 4, 0,
1495 doc: /* Remove some properties from text from START to END.
1496 The third argument PROPERTIES is a property list
1497 whose property names specify the properties to remove.
1498 \(The values stored in PROPERTIES are ignored.)
1499 If the optional fourth argument OBJECT is a buffer (or nil, which means
1500 the current buffer), START and END are buffer positions (integers or
1501 markers). If OBJECT is a string, START and END are 0-based indices into it.
1502 Return t if any property was actually removed, nil otherwise.
1504 Use set-text-properties if you want to remove all text properties. */)
1505 (start, end, properties, object)
1506 Lisp_Object start, end, properties, object;
1508 register INTERVAL i, unchanged;
1509 register int s, len, modified = 0;
1511 if (NILP (object))
1512 XSETBUFFER (object, current_buffer);
1514 i = validate_interval_range (object, &start, &end, soft);
1515 if (NULL_INTERVAL_P (i))
1516 return Qnil;
1518 s = XINT (start);
1519 len = XINT (end) - s;
1521 if (i->position != s)
1523 /* No properties on this first interval -- return if
1524 it covers the entire region. */
1525 if (! interval_has_some_properties (properties, i))
1527 int got = (LENGTH (i) - (s - i->position));
1528 if (got >= len)
1529 return Qnil;
1530 len -= got;
1531 i = next_interval (i);
1533 /* Split away the beginning of this interval; what we don't
1534 want to modify. */
1535 else
1537 unchanged = i;
1538 i = split_interval_right (unchanged, s - unchanged->position);
1539 copy_properties (unchanged, i);
1543 if (BUFFERP (object))
1544 modify_region (XBUFFER (object), XINT (start), XINT (end));
1546 /* We are at the beginning of an interval, with len to scan */
1547 for (;;)
1549 if (i == 0)
1550 abort ();
1552 if (LENGTH (i) >= len)
1554 if (! interval_has_some_properties (properties, i))
1555 return modified ? Qt : Qnil;
1557 if (LENGTH (i) == len)
1559 remove_properties (properties, Qnil, i, object);
1560 if (BUFFERP (object))
1561 signal_after_change (XINT (start), XINT (end) - XINT (start),
1562 XINT (end) - XINT (start));
1563 return Qt;
1566 /* i has the properties, and goes past the change limit */
1567 unchanged = i;
1568 i = split_interval_left (i, len);
1569 copy_properties (unchanged, i);
1570 remove_properties (properties, Qnil, i, object);
1571 if (BUFFERP (object))
1572 signal_after_change (XINT (start), XINT (end) - XINT (start),
1573 XINT (end) - XINT (start));
1574 return Qt;
1577 len -= LENGTH (i);
1578 modified += remove_properties (properties, Qnil, i, object);
1579 i = next_interval (i);
1583 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1584 Sremove_list_of_text_properties, 3, 4, 0,
1585 doc: /* Remove some properties from text from START to END.
1586 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1587 If the optional fourth argument OBJECT is a buffer (or nil, which means
1588 the current buffer), START and END are buffer positions (integers or
1589 markers). If OBJECT is a string, START and END are 0-based indices into it.
1590 Return t if any property was actually removed, nil otherwise. */)
1591 (start, end, list_of_properties, object)
1592 Lisp_Object start, end, list_of_properties, object;
1594 register INTERVAL i, unchanged;
1595 register int s, len, modified = 0;
1596 Lisp_Object properties;
1597 properties = list_of_properties;
1599 if (NILP (object))
1600 XSETBUFFER (object, current_buffer);
1602 i = validate_interval_range (object, &start, &end, soft);
1603 if (NULL_INTERVAL_P (i))
1604 return Qnil;
1606 s = XINT (start);
1607 len = XINT (end) - s;
1609 if (i->position != s)
1611 /* No properties on this first interval -- return if
1612 it covers the entire region. */
1613 if (! interval_has_some_properties_list (properties, i))
1615 int got = (LENGTH (i) - (s - i->position));
1616 if (got >= len)
1617 return Qnil;
1618 len -= got;
1619 i = next_interval (i);
1621 /* Split away the beginning of this interval; what we don't
1622 want to modify. */
1623 else
1625 unchanged = i;
1626 i = split_interval_right (unchanged, s - unchanged->position);
1627 copy_properties (unchanged, i);
1631 /* We are at the beginning of an interval, with len to scan.
1632 The flag `modified' records if changes have been made.
1633 When object is a buffer, we must call modify_region before changes are
1634 made and signal_after_change when we are done.
1635 We call modify_region before calling remove_properties iff modified == 0,
1636 and we call signal_after_change before returning iff modified != 0. */
1637 for (;;)
1639 if (i == 0)
1640 abort ();
1642 if (LENGTH (i) >= len)
1644 if (! interval_has_some_properties_list (properties, i))
1645 if (modified)
1647 if (BUFFERP (object))
1648 signal_after_change (XINT (start), XINT (end) - XINT (start),
1649 XINT (end) - XINT (start));
1650 return Qt;
1652 else
1653 return Qnil;
1655 if (LENGTH (i) == len)
1657 if (!modified && BUFFERP (object))
1658 modify_region (XBUFFER (object), XINT (start), XINT (end));
1659 remove_properties (Qnil, properties, i, object);
1660 if (BUFFERP (object))
1661 signal_after_change (XINT (start), XINT (end) - XINT (start),
1662 XINT (end) - XINT (start));
1663 return Qt;
1666 /* i has the properties, and goes past the change limit */
1667 unchanged = i;
1668 i = split_interval_left (i, len);
1669 copy_properties (unchanged, i);
1670 if (!modified && BUFFERP (object))
1671 modify_region (XBUFFER (object), XINT (start), XINT (end));
1672 remove_properties (Qnil, properties, i, object);
1673 if (BUFFERP (object))
1674 signal_after_change (XINT (start), XINT (end) - XINT (start),
1675 XINT (end) - XINT (start));
1676 return Qt;
1679 if (interval_has_some_properties_list (properties, i))
1681 if (!modified && BUFFERP (object))
1682 modify_region (XBUFFER (object), XINT (start), XINT (end));
1683 remove_properties (Qnil, properties, i, object);
1684 modified = 1;
1686 len -= LENGTH (i);
1687 i = next_interval (i);
1691 DEFUN ("text-property-any", Ftext_property_any,
1692 Stext_property_any, 4, 5, 0,
1693 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1694 If so, return the position of the first character whose property PROPERTY
1695 is `eq' to VALUE. Otherwise return nil.
1696 If the optional fifth argument OBJECT is a buffer (or nil, which means
1697 the current buffer), START and END are buffer positions (integers or
1698 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1699 (start, end, property, value, object)
1700 Lisp_Object start, end, property, value, object;
1702 register INTERVAL i;
1703 register int e, pos;
1705 if (NILP (object))
1706 XSETBUFFER (object, current_buffer);
1707 i = validate_interval_range (object, &start, &end, soft);
1708 if (NULL_INTERVAL_P (i))
1709 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1710 e = XINT (end);
1712 while (! NULL_INTERVAL_P (i))
1714 if (i->position >= e)
1715 break;
1716 if (EQ (textget (i->plist, property), value))
1718 pos = i->position;
1719 if (pos < XINT (start))
1720 pos = XINT (start);
1721 return make_number (pos);
1723 i = next_interval (i);
1725 return Qnil;
1728 DEFUN ("text-property-not-all", Ftext_property_not_all,
1729 Stext_property_not_all, 4, 5, 0,
1730 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1731 If so, return the position of the first character whose property PROPERTY
1732 is not `eq' to VALUE. Otherwise, return nil.
1733 If the optional fifth argument OBJECT is a buffer (or nil, which means
1734 the current buffer), START and END are buffer positions (integers or
1735 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1736 (start, end, property, value, object)
1737 Lisp_Object start, end, property, value, object;
1739 register INTERVAL i;
1740 register int s, e;
1742 if (NILP (object))
1743 XSETBUFFER (object, current_buffer);
1744 i = validate_interval_range (object, &start, &end, soft);
1745 if (NULL_INTERVAL_P (i))
1746 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1747 s = XINT (start);
1748 e = XINT (end);
1750 while (! NULL_INTERVAL_P (i))
1752 if (i->position >= e)
1753 break;
1754 if (! EQ (textget (i->plist, property), value))
1756 if (i->position > s)
1757 s = i->position;
1758 return make_number (s);
1760 i = next_interval (i);
1762 return Qnil;
1766 /* Return the direction from which the text-property PROP would be
1767 inherited by any new text inserted at POS: 1 if it would be
1768 inherited from the char after POS, -1 if it would be inherited from
1769 the char before POS, and 0 if from neither.
1770 BUFFER can be either a buffer or nil (meaning current buffer). */
1773 text_property_stickiness (prop, pos, buffer)
1774 Lisp_Object prop, pos, buffer;
1776 Lisp_Object prev_pos, front_sticky;
1777 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1779 if (NILP (buffer))
1780 XSETBUFFER (buffer, current_buffer);
1782 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1783 /* Consider previous character. */
1785 Lisp_Object rear_non_sticky;
1787 prev_pos = make_number (XINT (pos) - 1);
1788 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1790 if (!NILP (CONSP (rear_non_sticky)
1791 ? Fmemq (prop, rear_non_sticky)
1792 : rear_non_sticky))
1793 /* PROP is rear-non-sticky. */
1794 is_rear_sticky = 0;
1796 else
1797 return 0;
1799 /* Consider following character. */
1800 /* This signals an arg-out-of-range error if pos is outside the
1801 buffer's accessible range. */
1802 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1804 if (EQ (front_sticky, Qt)
1805 || (CONSP (front_sticky)
1806 && !NILP (Fmemq (prop, front_sticky))))
1807 /* PROP is inherited from after. */
1808 is_front_sticky = 1;
1810 /* Simple cases, where the properties are consistent. */
1811 if (is_rear_sticky && !is_front_sticky)
1812 return -1;
1813 else if (!is_rear_sticky && is_front_sticky)
1814 return 1;
1815 else if (!is_rear_sticky && !is_front_sticky)
1816 return 0;
1818 /* The stickiness properties are inconsistent, so we have to
1819 disambiguate. Basically, rear-sticky wins, _except_ if the
1820 property that would be inherited has a value of nil, in which case
1821 front-sticky wins. */
1822 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1823 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1824 return 1;
1825 else
1826 return -1;
1830 /* I don't think this is the right interface to export; how often do you
1831 want to do something like this, other than when you're copying objects
1832 around?
1834 I think it would be better to have a pair of functions, one which
1835 returns the text properties of a region as a list of ranges and
1836 plists, and another which applies such a list to another object. */
1838 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1839 SRC and DEST may each refer to strings or buffers.
1840 Optional sixth argument PROP causes only that property to be copied.
1841 Properties are copied to DEST as if by `add-text-properties'.
1842 Return t if any property value actually changed, nil otherwise. */
1844 /* Note this can GC when DEST is a buffer. */
1846 Lisp_Object
1847 copy_text_properties (start, end, src, pos, dest, prop)
1848 Lisp_Object start, end, src, pos, dest, prop;
1850 INTERVAL i;
1851 Lisp_Object res;
1852 Lisp_Object stuff;
1853 Lisp_Object plist;
1854 int s, e, e2, p, len, modified = 0;
1855 struct gcpro gcpro1, gcpro2;
1857 i = validate_interval_range (src, &start, &end, soft);
1858 if (NULL_INTERVAL_P (i))
1859 return Qnil;
1861 CHECK_NUMBER_COERCE_MARKER (pos);
1863 Lisp_Object dest_start, dest_end;
1865 dest_start = pos;
1866 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1867 /* Apply this to a copy of pos; it will try to increment its arguments,
1868 which we don't want. */
1869 validate_interval_range (dest, &dest_start, &dest_end, soft);
1872 s = XINT (start);
1873 e = XINT (end);
1874 p = XINT (pos);
1876 stuff = Qnil;
1878 while (s < e)
1880 e2 = i->position + LENGTH (i);
1881 if (e2 > e)
1882 e2 = e;
1883 len = e2 - s;
1885 plist = i->plist;
1886 if (! NILP (prop))
1887 while (! NILP (plist))
1889 if (EQ (Fcar (plist), prop))
1891 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1892 break;
1894 plist = Fcdr (Fcdr (plist));
1896 if (! NILP (plist))
1898 /* Must defer modifications to the interval tree in case src
1899 and dest refer to the same string or buffer. */
1900 stuff = Fcons (Fcons (make_number (p),
1901 Fcons (make_number (p + len),
1902 Fcons (plist, Qnil))),
1903 stuff);
1906 i = next_interval (i);
1907 if (NULL_INTERVAL_P (i))
1908 break;
1910 p += len;
1911 s = i->position;
1914 GCPRO2 (stuff, dest);
1916 while (! NILP (stuff))
1918 res = Fcar (stuff);
1919 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1920 Fcar (Fcdr (Fcdr (res))), dest);
1921 if (! NILP (res))
1922 modified++;
1923 stuff = Fcdr (stuff);
1926 UNGCPRO;
1928 return modified ? Qt : Qnil;
1932 /* Return a list representing the text properties of OBJECT between
1933 START and END. if PROP is non-nil, report only on that property.
1934 Each result list element has the form (S E PLIST), where S and E
1935 are positions in OBJECT and PLIST is a property list containing the
1936 text properties of OBJECT between S and E. Value is nil if OBJECT
1937 doesn't contain text properties between START and END. */
1939 Lisp_Object
1940 text_property_list (object, start, end, prop)
1941 Lisp_Object object, start, end, prop;
1943 struct interval *i;
1944 Lisp_Object result;
1946 result = Qnil;
1948 i = validate_interval_range (object, &start, &end, soft);
1949 if (!NULL_INTERVAL_P (i))
1951 int s = XINT (start);
1952 int e = XINT (end);
1954 while (s < e)
1956 int interval_end, len;
1957 Lisp_Object plist;
1959 interval_end = i->position + LENGTH (i);
1960 if (interval_end > e)
1961 interval_end = e;
1962 len = interval_end - s;
1964 plist = i->plist;
1966 if (!NILP (prop))
1967 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1968 if (EQ (Fcar (plist), prop))
1970 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1971 break;
1974 if (!NILP (plist))
1975 result = Fcons (Fcons (make_number (s),
1976 Fcons (make_number (s + len),
1977 Fcons (plist, Qnil))),
1978 result);
1980 i = next_interval (i);
1981 if (NULL_INTERVAL_P (i))
1982 break;
1983 s = i->position;
1987 return result;
1991 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1992 (START END PLIST), where START and END are positions and PLIST is a
1993 property list containing the text properties to add. Adjust START
1994 and END positions by DELTA before adding properties. Value is
1995 non-zero if OBJECT was modified. */
1998 add_text_properties_from_list (object, list, delta)
1999 Lisp_Object object, list, delta;
2001 struct gcpro gcpro1, gcpro2;
2002 int modified_p = 0;
2004 GCPRO2 (list, object);
2006 for (; CONSP (list); list = XCDR (list))
2008 Lisp_Object item, start, end, plist, tem;
2010 item = XCAR (list);
2011 start = make_number (XINT (XCAR (item)) + XINT (delta));
2012 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
2013 plist = XCAR (XCDR (XCDR (item)));
2015 tem = Fadd_text_properties (start, end, plist, object);
2016 if (!NILP (tem))
2017 modified_p = 1;
2020 UNGCPRO;
2021 return modified_p;
2026 /* Modify end-points of ranges in LIST destructively. LIST is a list
2027 as returned from text_property_list. Change end-points equal to
2028 OLD_END to NEW_END. */
2030 void
2031 extend_property_ranges (list, old_end, new_end)
2032 Lisp_Object list, old_end, new_end;
2034 for (; CONSP (list); list = XCDR (list))
2036 Lisp_Object item, end;
2038 item = XCAR (list);
2039 end = XCAR (XCDR (item));
2041 if (EQ (end, old_end))
2042 XSETCAR (XCDR (item), new_end);
2048 /* Call the modification hook functions in LIST, each with START and END. */
2050 static void
2051 call_mod_hooks (list, start, end)
2052 Lisp_Object list, start, end;
2054 struct gcpro gcpro1;
2055 GCPRO1 (list);
2056 while (!NILP (list))
2058 call2 (Fcar (list), start, end);
2059 list = Fcdr (list);
2061 UNGCPRO;
2064 /* Check for read-only intervals between character positions START ... END,
2065 in BUF, and signal an error if we find one.
2067 Then check for any modification hooks in the range.
2068 Create a list of all these hooks in lexicographic order,
2069 eliminating consecutive extra copies of the same hook. Then call
2070 those hooks in order, with START and END - 1 as arguments. */
2072 void
2073 verify_interval_modification (buf, start, end)
2074 struct buffer *buf;
2075 int start, end;
2077 register INTERVAL intervals = BUF_INTERVALS (buf);
2078 register INTERVAL i;
2079 Lisp_Object hooks;
2080 register Lisp_Object prev_mod_hooks;
2081 Lisp_Object mod_hooks;
2082 struct gcpro gcpro1;
2084 hooks = Qnil;
2085 prev_mod_hooks = Qnil;
2086 mod_hooks = Qnil;
2088 interval_insert_behind_hooks = Qnil;
2089 interval_insert_in_front_hooks = Qnil;
2091 if (NULL_INTERVAL_P (intervals))
2092 return;
2094 if (start > end)
2096 int temp = start;
2097 start = end;
2098 end = temp;
2101 /* For an insert operation, check the two chars around the position. */
2102 if (start == end)
2104 INTERVAL prev = NULL;
2105 Lisp_Object before, after;
2107 /* Set I to the interval containing the char after START,
2108 and PREV to the interval containing the char before START.
2109 Either one may be null. They may be equal. */
2110 i = find_interval (intervals, start);
2112 if (start == BUF_BEGV (buf))
2113 prev = 0;
2114 else if (i->position == start)
2115 prev = previous_interval (i);
2116 else if (i->position < start)
2117 prev = i;
2118 if (start == BUF_ZV (buf))
2119 i = 0;
2121 /* If Vinhibit_read_only is set and is not a list, we can
2122 skip the read_only checks. */
2123 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2125 /* If I and PREV differ we need to check for the read-only
2126 property together with its stickiness. If either I or
2127 PREV are 0, this check is all we need.
2128 We have to take special care, since read-only may be
2129 indirectly defined via the category property. */
2130 if (i != prev)
2132 if (! NULL_INTERVAL_P (i))
2134 after = textget (i->plist, Qread_only);
2136 /* If interval I is read-only and read-only is
2137 front-sticky, inhibit insertion.
2138 Check for read-only as well as category. */
2139 if (! NILP (after)
2140 && NILP (Fmemq (after, Vinhibit_read_only)))
2142 Lisp_Object tem;
2144 tem = textget (i->plist, Qfront_sticky);
2145 if (TMEM (Qread_only, tem)
2146 || (NILP (Fplist_get (i->plist, Qread_only))
2147 && TMEM (Qcategory, tem)))
2148 text_read_only (after);
2152 if (! NULL_INTERVAL_P (prev))
2154 before = textget (prev->plist, Qread_only);
2156 /* If interval PREV is read-only and read-only isn't
2157 rear-nonsticky, inhibit insertion.
2158 Check for read-only as well as category. */
2159 if (! NILP (before)
2160 && NILP (Fmemq (before, Vinhibit_read_only)))
2162 Lisp_Object tem;
2164 tem = textget (prev->plist, Qrear_nonsticky);
2165 if (! TMEM (Qread_only, tem)
2166 && (! NILP (Fplist_get (prev->plist,Qread_only))
2167 || ! TMEM (Qcategory, tem)))
2168 text_read_only (before);
2172 else if (! NULL_INTERVAL_P (i))
2174 after = textget (i->plist, Qread_only);
2176 /* If interval I is read-only and read-only is
2177 front-sticky, inhibit insertion.
2178 Check for read-only as well as category. */
2179 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2181 Lisp_Object tem;
2183 tem = textget (i->plist, Qfront_sticky);
2184 if (TMEM (Qread_only, tem)
2185 || (NILP (Fplist_get (i->plist, Qread_only))
2186 && TMEM (Qcategory, tem)))
2187 text_read_only (after);
2189 tem = textget (prev->plist, Qrear_nonsticky);
2190 if (! TMEM (Qread_only, tem)
2191 && (! NILP (Fplist_get (prev->plist, Qread_only))
2192 || ! TMEM (Qcategory, tem)))
2193 text_read_only (after);
2198 /* Run both insert hooks (just once if they're the same). */
2199 if (!NULL_INTERVAL_P (prev))
2200 interval_insert_behind_hooks
2201 = textget (prev->plist, Qinsert_behind_hooks);
2202 if (!NULL_INTERVAL_P (i))
2203 interval_insert_in_front_hooks
2204 = textget (i->plist, Qinsert_in_front_hooks);
2206 else
2208 /* Loop over intervals on or next to START...END,
2209 collecting their hooks. */
2211 i = find_interval (intervals, start);
2214 if (! INTERVAL_WRITABLE_P (i))
2215 text_read_only (textget (i->plist, Qread_only));
2217 if (!inhibit_modification_hooks)
2219 mod_hooks = textget (i->plist, Qmodification_hooks);
2220 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2222 hooks = Fcons (mod_hooks, hooks);
2223 prev_mod_hooks = mod_hooks;
2227 i = next_interval (i);
2229 /* Keep going thru the interval containing the char before END. */
2230 while (! NULL_INTERVAL_P (i) && i->position < end);
2232 if (!inhibit_modification_hooks)
2234 GCPRO1 (hooks);
2235 hooks = Fnreverse (hooks);
2236 while (! EQ (hooks, Qnil))
2238 call_mod_hooks (Fcar (hooks), make_number (start),
2239 make_number (end));
2240 hooks = Fcdr (hooks);
2242 UNGCPRO;
2247 /* Run the interval hooks for an insertion on character range START ... END.
2248 verify_interval_modification chose which hooks to run;
2249 this function is called after the insertion happens
2250 so it can indicate the range of inserted text. */
2252 void
2253 report_interval_modification (start, end)
2254 Lisp_Object start, end;
2256 if (! NILP (interval_insert_behind_hooks))
2257 call_mod_hooks (interval_insert_behind_hooks, start, end);
2258 if (! NILP (interval_insert_in_front_hooks)
2259 && ! EQ (interval_insert_in_front_hooks,
2260 interval_insert_behind_hooks))
2261 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2264 void
2265 syms_of_textprop ()
2267 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2268 doc: /* Property-list used as default values.
2269 The value of a property in this list is seen as the value for every
2270 character that does not have its own value for that property. */);
2271 Vdefault_text_properties = Qnil;
2273 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2274 doc: /* Alist of alternative properties for properties without a value.
2275 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2276 If a piece of text has no direct value for a particular property, then
2277 this alist is consulted. If that property appears in the alist, then
2278 the first non-nil value from the associated alternative properties is
2279 returned. */);
2280 Vchar_property_alias_alist = Qnil;
2282 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2283 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2284 This also inhibits the use of the `intangible' text property. */);
2285 Vinhibit_point_motion_hooks = Qnil;
2287 DEFVAR_LISP ("text-property-default-nonsticky",
2288 &Vtext_property_default_nonsticky,
2289 doc: /* Alist of properties vs the corresponding non-stickinesses.
2290 Each element has the form (PROPERTY . NONSTICKINESS).
2292 If a character in a buffer has PROPERTY, new text inserted adjacent to
2293 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2294 inherits it if NONSTICKINESS is nil. The front-sticky and
2295 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2296 /* Text property `syntax-table' should be nonsticky by default. */
2297 Vtext_property_default_nonsticky
2298 = Fcons (Fcons (intern ("syntax-table"), Qt), Qnil);
2300 staticpro (&interval_insert_behind_hooks);
2301 staticpro (&interval_insert_in_front_hooks);
2302 interval_insert_behind_hooks = Qnil;
2303 interval_insert_in_front_hooks = Qnil;
2306 /* Common attributes one might give text */
2308 staticpro (&Qforeground);
2309 Qforeground = intern ("foreground");
2310 staticpro (&Qbackground);
2311 Qbackground = intern ("background");
2312 staticpro (&Qfont);
2313 Qfont = intern ("font");
2314 staticpro (&Qstipple);
2315 Qstipple = intern ("stipple");
2316 staticpro (&Qunderline);
2317 Qunderline = intern ("underline");
2318 staticpro (&Qread_only);
2319 Qread_only = intern ("read-only");
2320 staticpro (&Qinvisible);
2321 Qinvisible = intern ("invisible");
2322 staticpro (&Qintangible);
2323 Qintangible = intern ("intangible");
2324 staticpro (&Qcategory);
2325 Qcategory = intern ("category");
2326 staticpro (&Qlocal_map);
2327 Qlocal_map = intern ("local-map");
2328 staticpro (&Qfront_sticky);
2329 Qfront_sticky = intern ("front-sticky");
2330 staticpro (&Qrear_nonsticky);
2331 Qrear_nonsticky = intern ("rear-nonsticky");
2332 staticpro (&Qmouse_face);
2333 Qmouse_face = intern ("mouse-face");
2335 /* Properties that text might use to specify certain actions */
2337 staticpro (&Qmouse_left);
2338 Qmouse_left = intern ("mouse-left");
2339 staticpro (&Qmouse_entered);
2340 Qmouse_entered = intern ("mouse-entered");
2341 staticpro (&Qpoint_left);
2342 Qpoint_left = intern ("point-left");
2343 staticpro (&Qpoint_entered);
2344 Qpoint_entered = intern ("point-entered");
2346 defsubr (&Stext_properties_at);
2347 defsubr (&Sget_text_property);
2348 defsubr (&Sget_char_property);
2349 defsubr (&Sget_char_property_and_overlay);
2350 defsubr (&Snext_char_property_change);
2351 defsubr (&Sprevious_char_property_change);
2352 defsubr (&Snext_single_char_property_change);
2353 defsubr (&Sprevious_single_char_property_change);
2354 defsubr (&Snext_property_change);
2355 defsubr (&Snext_single_property_change);
2356 defsubr (&Sprevious_property_change);
2357 defsubr (&Sprevious_single_property_change);
2358 defsubr (&Sadd_text_properties);
2359 defsubr (&Sput_text_property);
2360 defsubr (&Sset_text_properties);
2361 defsubr (&Sremove_text_properties);
2362 defsubr (&Sremove_list_of_text_properties);
2363 defsubr (&Stext_property_any);
2364 defsubr (&Stext_property_not_all);
2365 /* defsubr (&Serase_text_properties); */
2366 /* defsubr (&Scopy_text_properties); */
2369 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2370 (do not change this comment) */