Nuke arch-tags.
[emacs.git] / src / textprop.c
bloba2794264d906d903ae5f346cac814ceb1fef7e4b
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 3 of the License, or
10 (at your option) 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. If not, see <http://www.gnu.org/licenses/>. */
20 #include <config.h>
21 #include <setjmp.h>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "buffer.h"
25 #include "window.h"
27 #ifndef NULL
28 #define NULL (void *)0
29 #endif
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
41 set_properties needs to deal with the interval property cache.
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
50 /* Types of hooks. */
51 Lisp_Object Qmouse_left;
52 Lisp_Object Qmouse_entered;
53 Lisp_Object Qpoint_left;
54 Lisp_Object Qpoint_entered;
55 Lisp_Object Qcategory;
56 Lisp_Object Qlocal_map;
58 /* Visual properties text (including strings) may have. */
59 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
60 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
61 Lisp_Object Qminibuffer_prompt;
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 (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 (Lisp_Object propval)
90 if (STRINGP (propval))
91 xsignal1 (Qtext_read_only, propval);
93 xsignal0 (Qtext_read_only);
98 /* Extract the interval at the position pointed to by BEGIN from
99 OBJECT, a string or buffer. Additionally, check that the positions
100 pointed to by BEGIN and END are within the bounds of OBJECT, and
101 reverse them if *BEGIN is greater than *END. The objects pointed
102 to by BEGIN and END may be integers or markers; if the latter, they
103 are coerced to integers.
105 When OBJECT is a string, we increment *BEGIN and *END
106 to make them origin-one.
108 Note that buffer points don't correspond to interval indices.
109 For example, point-max is 1 greater than the index of the last
110 character. This difference is handled in the caller, which uses
111 the validated points to determine a length, and operates on that.
112 Exceptions are Ftext_properties_at, Fnext_property_change, and
113 Fprevious_property_change which call this function with BEGIN == END.
114 Handle this case specially.
116 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
117 create an interval tree for OBJECT if one doesn't exist, provided
118 the object actually contains text. In the current design, if there
119 is no text, there can be no text properties. */
121 #define soft 0
122 #define hard 1
124 INTERVAL
125 validate_interval_range (Lisp_Object object, Lisp_Object *begin, Lisp_Object *end, int force)
127 register INTERVAL i;
128 EMACS_INT searchpos;
130 CHECK_STRING_OR_BUFFER (object);
131 CHECK_NUMBER_COERCE_MARKER (*begin);
132 CHECK_NUMBER_COERCE_MARKER (*end);
134 /* If we are asked for a point, but from a subr which operates
135 on a range, then return nothing. */
136 if (EQ (*begin, *end) && begin != end)
137 return NULL_INTERVAL;
139 if (XINT (*begin) > XINT (*end))
141 Lisp_Object n;
142 n = *begin;
143 *begin = *end;
144 *end = n;
147 if (BUFFERP (object))
149 register struct buffer *b = XBUFFER (object);
151 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
152 && XINT (*end) <= BUF_ZV (b)))
153 args_out_of_range (*begin, *end);
154 i = BUF_INTERVALS (b);
156 /* If there's no text, there are no properties. */
157 if (BUF_BEGV (b) == BUF_ZV (b))
158 return NULL_INTERVAL;
160 searchpos = XINT (*begin);
162 else
164 EMACS_INT len = SCHARS (object);
166 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
167 && XINT (*end) <= len))
168 args_out_of_range (*begin, *end);
169 XSETFASTINT (*begin, XFASTINT (*begin));
170 if (begin != end)
171 XSETFASTINT (*end, XFASTINT (*end));
172 i = STRING_INTERVALS (object);
174 if (len == 0)
175 return NULL_INTERVAL;
177 searchpos = XINT (*begin);
180 if (NULL_INTERVAL_P (i))
181 return (force ? create_root_interval (object) : i);
183 return find_interval (i, searchpos);
186 /* Validate LIST as a property list. If LIST is not a list, then
187 make one consisting of (LIST nil). Otherwise, verify that LIST
188 is even numbered and thus suitable as a plist. */
190 static Lisp_Object
191 validate_plist (Lisp_Object list)
193 if (NILP (list))
194 return Qnil;
196 if (CONSP (list))
198 register int i;
199 register Lisp_Object tail;
200 for (i = 0, tail = list; CONSP (tail); i++)
202 tail = XCDR (tail);
203 QUIT;
205 if (i & 1)
206 error ("Odd length text property list");
207 return list;
210 return Fcons (list, Fcons (Qnil, Qnil));
213 /* Return nonzero if interval I has all the properties,
214 with the same values, of list PLIST. */
216 static int
217 interval_has_all_properties (Lisp_Object plist, INTERVAL i)
219 register Lisp_Object tail1, tail2, sym1;
220 register int found;
222 /* Go through each element of PLIST. */
223 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
225 sym1 = XCAR (tail1);
226 found = 0;
228 /* Go through I's plist, looking for sym1 */
229 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
230 if (EQ (sym1, XCAR (tail2)))
232 /* Found the same property on both lists. If the
233 values are unequal, return zero. */
234 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
235 return 0;
237 /* Property has same value on both lists; go to next one. */
238 found = 1;
239 break;
242 if (! found)
243 return 0;
246 return 1;
249 /* Return nonzero if the plist of interval I has any of the
250 properties of PLIST, regardless of their values. */
252 static INLINE int
253 interval_has_some_properties (Lisp_Object plist, INTERVAL i)
255 register Lisp_Object tail1, tail2, sym;
257 /* Go through each element of PLIST. */
258 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
260 sym = XCAR (tail1);
262 /* Go through i's plist, looking for tail1 */
263 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
264 if (EQ (sym, XCAR (tail2)))
265 return 1;
268 return 0;
271 /* Return nonzero if the plist of interval I has any of the
272 property names in LIST, regardless of their values. */
274 static INLINE int
275 interval_has_some_properties_list (Lisp_Object list, INTERVAL i)
277 register Lisp_Object tail1, tail2, sym;
279 /* Go through each element of LIST. */
280 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
282 sym = Fcar (tail1);
284 /* Go through i's plist, looking for tail1 */
285 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
286 if (EQ (sym, XCAR (tail2)))
287 return 1;
290 return 0;
293 /* Changing the plists of individual intervals. */
295 /* Return the value of PROP in property-list PLIST, or Qunbound if it
296 has none. */
297 static Lisp_Object
298 property_value (Lisp_Object plist, Lisp_Object prop)
300 Lisp_Object value;
302 while (PLIST_ELT_P (plist, value))
303 if (EQ (XCAR (plist), prop))
304 return XCAR (value);
305 else
306 plist = XCDR (value);
308 return Qunbound;
311 /* Set the properties of INTERVAL to PROPERTIES,
312 and record undo info for the previous values.
313 OBJECT is the string or buffer that INTERVAL belongs to. */
315 static void
316 set_properties (Lisp_Object properties, INTERVAL interval, Lisp_Object object)
318 Lisp_Object sym, value;
320 if (BUFFERP (object))
322 /* For each property in the old plist which is missing from PROPERTIES,
323 or has a different value in PROPERTIES, make an undo record. */
324 for (sym = interval->plist;
325 PLIST_ELT_P (sym, value);
326 sym = XCDR (value))
327 if (! EQ (property_value (properties, XCAR (sym)),
328 XCAR (value)))
330 record_property_change (interval->position, LENGTH (interval),
331 XCAR (sym), XCAR (value),
332 object);
335 /* For each new property that has no value at all in the old plist,
336 make an undo record binding it to nil, so it will be removed. */
337 for (sym = properties;
338 PLIST_ELT_P (sym, value);
339 sym = XCDR (value))
340 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
342 record_property_change (interval->position, LENGTH (interval),
343 XCAR (sym), Qnil,
344 object);
348 /* Store new properties. */
349 interval->plist = Fcopy_sequence (properties);
352 /* Add the properties of PLIST to the interval I, or set
353 the value of I's property to the value of the property on PLIST
354 if they are different.
356 OBJECT should be the string or buffer the interval is in.
358 Return nonzero if this changes I (i.e., if any members of PLIST
359 are actually added to I's plist) */
361 static int
362 add_properties (Lisp_Object plist, INTERVAL i, Lisp_Object object)
364 Lisp_Object tail1, tail2, sym1, val1;
365 register int changed = 0;
366 register int found;
367 struct gcpro gcpro1, gcpro2, gcpro3;
369 tail1 = plist;
370 sym1 = Qnil;
371 val1 = Qnil;
372 /* No need to protect OBJECT, because we can GC only in the case
373 where it is a buffer, and live buffers are always protected.
374 I and its plist are also protected, via OBJECT. */
375 GCPRO3 (tail1, sym1, val1);
377 /* Go through each element of PLIST. */
378 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
380 sym1 = XCAR (tail1);
381 val1 = Fcar (XCDR (tail1));
382 found = 0;
384 /* Go through I's plist, looking for sym1 */
385 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
386 if (EQ (sym1, XCAR (tail2)))
388 /* No need to gcpro, because tail2 protects this
389 and it must be a cons cell (we get an error otherwise). */
390 register Lisp_Object this_cdr;
392 this_cdr = XCDR (tail2);
393 /* Found the property. Now check its value. */
394 found = 1;
396 /* The properties have the same value on both lists.
397 Continue to the next property. */
398 if (EQ (val1, Fcar (this_cdr)))
399 break;
401 /* Record this change in the buffer, for undo purposes. */
402 if (BUFFERP (object))
404 record_property_change (i->position, LENGTH (i),
405 sym1, Fcar (this_cdr), object);
408 /* I's property has a different value -- change it */
409 Fsetcar (this_cdr, val1);
410 changed++;
411 break;
414 if (! found)
416 /* Record this change in the buffer, for undo purposes. */
417 if (BUFFERP (object))
419 record_property_change (i->position, LENGTH (i),
420 sym1, Qnil, object);
422 i->plist = Fcons (sym1, Fcons (val1, i->plist));
423 changed++;
427 UNGCPRO;
429 return changed;
432 /* For any members of PLIST, or LIST,
433 which are properties of I, remove them from I's plist.
434 (If PLIST is non-nil, use that, otherwise use LIST.)
435 OBJECT is the string or buffer containing I. */
437 static int
438 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
440 register Lisp_Object tail1, tail2, sym, current_plist;
441 register int changed = 0;
443 /* Nonzero means tail1 is a plist, otherwise it is a list. */
444 int use_plist;
446 current_plist = i->plist;
448 if (! NILP (plist))
449 tail1 = plist, use_plist = 1;
450 else
451 tail1 = list, use_plist = 0;
453 /* Go through each element of LIST or PLIST. */
454 while (CONSP (tail1))
456 sym = XCAR (tail1);
458 /* First, remove the symbol if it's at the head of the list */
459 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
461 if (BUFFERP (object))
462 record_property_change (i->position, LENGTH (i),
463 sym, XCAR (XCDR (current_plist)),
464 object);
466 current_plist = XCDR (XCDR (current_plist));
467 changed++;
470 /* Go through I's plist, looking for SYM. */
471 tail2 = current_plist;
472 while (! NILP (tail2))
474 register Lisp_Object this;
475 this = XCDR (XCDR (tail2));
476 if (CONSP (this) && EQ (sym, XCAR (this)))
478 if (BUFFERP (object))
479 record_property_change (i->position, LENGTH (i),
480 sym, XCAR (XCDR (this)), object);
482 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
483 changed++;
485 tail2 = this;
488 /* Advance thru TAIL1 one way or the other. */
489 tail1 = XCDR (tail1);
490 if (use_plist && CONSP (tail1))
491 tail1 = XCDR (tail1);
494 if (changed)
495 i->plist = current_plist;
496 return changed;
499 #if 0
500 /* Remove all properties from interval I. Return non-zero
501 if this changes the interval. */
503 static INLINE int
504 erase_properties (INTERVAL i)
506 if (NILP (i->plist))
507 return 0;
509 i->plist = Qnil;
510 return 1;
512 #endif
514 /* Returns the interval of POSITION in OBJECT.
515 POSITION is BEG-based. */
517 INTERVAL
518 interval_of (int position, Lisp_Object object)
520 register INTERVAL i;
521 EMACS_INT beg, end;
523 if (NILP (object))
524 XSETBUFFER (object, current_buffer);
525 else if (EQ (object, Qt))
526 return NULL_INTERVAL;
528 CHECK_STRING_OR_BUFFER (object);
530 if (BUFFERP (object))
532 register struct buffer *b = XBUFFER (object);
534 beg = BUF_BEGV (b);
535 end = BUF_ZV (b);
536 i = BUF_INTERVALS (b);
538 else
540 beg = 0;
541 end = SCHARS (object);
542 i = STRING_INTERVALS (object);
545 if (!(beg <= position && position <= end))
546 args_out_of_range (make_number (position), make_number (position));
547 if (beg == end || NULL_INTERVAL_P (i))
548 return NULL_INTERVAL;
550 return find_interval (i, position);
553 DEFUN ("text-properties-at", Ftext_properties_at,
554 Stext_properties_at, 1, 2, 0,
555 doc: /* Return the list of properties of the character at POSITION in OBJECT.
556 If the optional second argument OBJECT is a buffer (or nil, which means
557 the current buffer), POSITION is a buffer position (integer or marker).
558 If OBJECT is a string, POSITION is a 0-based index into it.
559 If POSITION is at the end of OBJECT, the value is nil. */)
560 (Lisp_Object position, Lisp_Object object)
562 register INTERVAL i;
564 if (NILP (object))
565 XSETBUFFER (object, current_buffer);
567 i = validate_interval_range (object, &position, &position, soft);
568 if (NULL_INTERVAL_P (i))
569 return Qnil;
570 /* If POSITION is at the end of the interval,
571 it means it's the end of OBJECT.
572 There are no properties at the very end,
573 since no character follows. */
574 if (XINT (position) == LENGTH (i) + i->position)
575 return Qnil;
577 return i->plist;
580 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
581 doc: /* Return the value of POSITION's property PROP, in OBJECT.
582 OBJECT is optional and defaults to the current buffer.
583 If POSITION is at the end of OBJECT, the value is nil. */)
584 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
586 return textget (Ftext_properties_at (position, object), prop);
589 /* Return the value of char's property PROP, in OBJECT at POSITION.
590 OBJECT is optional and defaults to the current buffer.
591 If OVERLAY is non-0, then in the case that the returned property is from
592 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
593 returned in *OVERLAY.
594 If POSITION is at the end of OBJECT, the value is nil.
595 If OBJECT is a buffer, then overlay properties are considered as well as
596 text properties.
597 If OBJECT is a window, then that window's buffer is used, but
598 window-specific overlays are considered only if they are associated
599 with OBJECT. */
600 Lisp_Object
601 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
603 struct window *w = 0;
605 CHECK_NUMBER_COERCE_MARKER (position);
607 if (NILP (object))
608 XSETBUFFER (object, current_buffer);
610 if (WINDOWP (object))
612 w = XWINDOW (object);
613 object = w->buffer;
615 if (BUFFERP (object))
617 int noverlays;
618 Lisp_Object *overlay_vec;
619 struct buffer *obuf = current_buffer;
621 if (XINT (position) < BUF_BEGV (XBUFFER (object))
622 || XINT (position) > BUF_ZV (XBUFFER (object)))
623 xsignal1 (Qargs_out_of_range, position);
625 set_buffer_temp (XBUFFER (object));
627 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
628 noverlays = sort_overlays (overlay_vec, noverlays, w);
630 set_buffer_temp (obuf);
632 /* Now check the overlays in order of decreasing priority. */
633 while (--noverlays >= 0)
635 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
636 if (!NILP (tem))
638 if (overlay)
639 /* Return the overlay we got the property from. */
640 *overlay = overlay_vec[noverlays];
641 return tem;
646 if (overlay)
647 /* Indicate that the return value is not from an overlay. */
648 *overlay = Qnil;
650 /* Not a buffer, or no appropriate overlay, so fall through to the
651 simpler case. */
652 return Fget_text_property (position, prop, object);
655 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
656 doc: /* Return the value of POSITION's property PROP, in OBJECT.
657 Both overlay properties and text properties are checked.
658 OBJECT is optional and defaults to the current buffer.
659 If POSITION is at the end of OBJECT, the value is nil.
660 If OBJECT is a buffer, then overlay properties are considered as well as
661 text properties.
662 If OBJECT is a window, then that window's buffer is used, but window-specific
663 overlays are considered only if they are associated with OBJECT. */)
664 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
666 return get_char_property_and_overlay (position, prop, object, 0);
669 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
670 Sget_char_property_and_overlay, 2, 3, 0,
671 doc: /* Like `get-char-property', but with extra overlay information.
672 The value is a cons cell. Its car is the return value of `get-char-property'
673 with the same arguments--that is, the value of POSITION's property
674 PROP in OBJECT. Its cdr is the overlay in which the property was
675 found, or nil, if it was found as a text property or not found at all.
677 OBJECT is optional and defaults to the current buffer. OBJECT may be
678 a string, a buffer or a window. For strings, the cdr of the return
679 value is always nil, since strings do not have overlays. If OBJECT is
680 a window, then that window's buffer is used, but window-specific
681 overlays are considered only if they are associated with OBJECT. If
682 POSITION is at the end of OBJECT, both car and cdr are nil. */)
683 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
685 Lisp_Object overlay;
686 Lisp_Object val
687 = get_char_property_and_overlay (position, prop, object, &overlay);
688 return Fcons (val, overlay);
692 DEFUN ("next-char-property-change", Fnext_char_property_change,
693 Snext_char_property_change, 1, 2, 0,
694 doc: /* Return the position of next text property or overlay change.
695 This scans characters forward in the current buffer from POSITION till
696 it finds a change in some text property, or the beginning or end of an
697 overlay, and returns the position of that.
698 If none is found up to (point-max), the function returns (point-max).
700 If the optional second argument LIMIT is non-nil, don't search
701 past position LIMIT; return LIMIT if nothing is found before LIMIT.
702 LIMIT is a no-op if it is greater than (point-max). */)
703 (Lisp_Object position, Lisp_Object limit)
705 Lisp_Object temp;
707 temp = Fnext_overlay_change (position);
708 if (! NILP (limit))
710 CHECK_NUMBER_COERCE_MARKER (limit);
711 if (XINT (limit) < XINT (temp))
712 temp = limit;
714 return Fnext_property_change (position, Qnil, temp);
717 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
718 Sprevious_char_property_change, 1, 2, 0,
719 doc: /* Return the position of previous text property or overlay change.
720 Scans characters backward in the current buffer from POSITION till it
721 finds a change in some text property, or the beginning or end of an
722 overlay, and returns the position of that.
723 If none is found since (point-min), the function returns (point-min).
725 If the optional second argument LIMIT is non-nil, don't search
726 past position LIMIT; return LIMIT if nothing is found before LIMIT.
727 LIMIT is a no-op if it is less than (point-min). */)
728 (Lisp_Object position, Lisp_Object limit)
730 Lisp_Object temp;
732 temp = Fprevious_overlay_change (position);
733 if (! NILP (limit))
735 CHECK_NUMBER_COERCE_MARKER (limit);
736 if (XINT (limit) > XINT (temp))
737 temp = limit;
739 return Fprevious_property_change (position, Qnil, temp);
743 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
744 Snext_single_char_property_change, 2, 4, 0,
745 doc: /* Return the position of next text property or overlay change for a specific property.
746 Scans characters forward from POSITION till it finds
747 a change in the PROP property, then returns the position of the change.
748 If the optional third argument OBJECT is a buffer (or nil, which means
749 the current buffer), POSITION is a buffer position (integer or marker).
750 If OBJECT is a string, POSITION is a 0-based index into it.
752 In a string, scan runs to the end of the string.
753 In a buffer, it runs to (point-max), and the value cannot exceed that.
755 The property values are compared with `eq'.
756 If the property is constant all the way to the end of OBJECT, return the
757 last valid position in OBJECT.
758 If the optional fourth argument LIMIT is non-nil, don't search
759 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
760 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
762 if (STRINGP (object))
764 position = Fnext_single_property_change (position, prop, object, limit);
765 if (NILP (position))
767 if (NILP (limit))
768 position = make_number (SCHARS (object));
769 else
771 CHECK_NUMBER (limit);
772 position = limit;
776 else
778 Lisp_Object initial_value, value;
779 int count = SPECPDL_INDEX ();
781 if (! NILP (object))
782 CHECK_BUFFER (object);
784 if (BUFFERP (object) && current_buffer != XBUFFER (object))
786 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
787 Fset_buffer (object);
790 CHECK_NUMBER_COERCE_MARKER (position);
792 initial_value = Fget_char_property (position, prop, object);
794 if (NILP (limit))
795 XSETFASTINT (limit, ZV);
796 else
797 CHECK_NUMBER_COERCE_MARKER (limit);
799 if (XFASTINT (position) >= XFASTINT (limit))
801 position = limit;
802 if (XFASTINT (position) > ZV)
803 XSETFASTINT (position, ZV);
805 else
806 while (1)
808 position = Fnext_char_property_change (position, limit);
809 if (XFASTINT (position) >= XFASTINT (limit))
811 position = limit;
812 break;
815 value = Fget_char_property (position, prop, object);
816 if (!EQ (value, initial_value))
817 break;
820 unbind_to (count, Qnil);
823 return position;
826 DEFUN ("previous-single-char-property-change",
827 Fprevious_single_char_property_change,
828 Sprevious_single_char_property_change, 2, 4, 0,
829 doc: /* Return the position of previous text property or overlay change for a specific property.
830 Scans characters backward from POSITION till it finds
831 a change in the PROP property, then returns the position of the change.
832 If the optional third argument OBJECT is a buffer (or nil, which means
833 the current buffer), POSITION is a buffer position (integer or marker).
834 If OBJECT is a string, POSITION is a 0-based index into it.
836 In a string, scan runs to the start of the string.
837 In a buffer, it runs to (point-min), and the value cannot be less than that.
839 The property values are compared with `eq'.
840 If the property is constant all the way to the start of OBJECT, return the
841 first valid position in OBJECT.
842 If the optional fourth argument LIMIT is non-nil, don't search
843 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
844 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
846 if (STRINGP (object))
848 position = Fprevious_single_property_change (position, prop, object, limit);
849 if (NILP (position))
851 if (NILP (limit))
852 position = make_number (0);
853 else
855 CHECK_NUMBER (limit);
856 position = limit;
860 else
862 int count = SPECPDL_INDEX ();
864 if (! NILP (object))
865 CHECK_BUFFER (object);
867 if (BUFFERP (object) && current_buffer != XBUFFER (object))
869 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
870 Fset_buffer (object);
873 CHECK_NUMBER_COERCE_MARKER (position);
875 if (NILP (limit))
876 XSETFASTINT (limit, BEGV);
877 else
878 CHECK_NUMBER_COERCE_MARKER (limit);
880 if (XFASTINT (position) <= XFASTINT (limit))
882 position = limit;
883 if (XFASTINT (position) < BEGV)
884 XSETFASTINT (position, BEGV);
886 else
888 Lisp_Object initial_value
889 = Fget_char_property (make_number (XFASTINT (position) - 1),
890 prop, object);
892 while (1)
894 position = Fprevious_char_property_change (position, limit);
896 if (XFASTINT (position) <= XFASTINT (limit))
898 position = limit;
899 break;
901 else
903 Lisp_Object value
904 = Fget_char_property (make_number (XFASTINT (position) - 1),
905 prop, object);
907 if (!EQ (value, initial_value))
908 break;
913 unbind_to (count, Qnil);
916 return position;
919 DEFUN ("next-property-change", Fnext_property_change,
920 Snext_property_change, 1, 3, 0,
921 doc: /* Return the position of next property change.
922 Scans characters forward from POSITION in OBJECT till it finds
923 a change in some text property, then returns the position of the change.
924 If the optional second argument OBJECT is a buffer (or nil, which means
925 the current buffer), POSITION is a buffer position (integer or marker).
926 If OBJECT is a string, POSITION is a 0-based index into it.
927 Return nil if the property is constant all the way to the end of OBJECT.
928 If the value is non-nil, it is a position greater than POSITION, never equal.
930 If the optional third argument LIMIT is non-nil, don't search
931 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
932 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
934 register INTERVAL i, next;
936 if (NILP (object))
937 XSETBUFFER (object, current_buffer);
939 if (!NILP (limit) && !EQ (limit, Qt))
940 CHECK_NUMBER_COERCE_MARKER (limit);
942 i = validate_interval_range (object, &position, &position, soft);
944 /* If LIMIT is t, return start of next interval--don't
945 bother checking further intervals. */
946 if (EQ (limit, Qt))
948 if (NULL_INTERVAL_P (i))
949 next = i;
950 else
951 next = next_interval (i);
953 if (NULL_INTERVAL_P (next))
954 XSETFASTINT (position, (STRINGP (object)
955 ? SCHARS (object)
956 : BUF_ZV (XBUFFER (object))));
957 else
958 XSETFASTINT (position, next->position);
959 return position;
962 if (NULL_INTERVAL_P (i))
963 return limit;
965 next = next_interval (i);
967 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
968 && (NILP (limit) || next->position < XFASTINT (limit)))
969 next = next_interval (next);
971 if (NULL_INTERVAL_P (next)
972 || (next->position
973 >= (INTEGERP (limit)
974 ? XFASTINT (limit)
975 : (STRINGP (object)
976 ? SCHARS (object)
977 : BUF_ZV (XBUFFER (object))))))
978 return limit;
979 else
980 return make_number (next->position);
983 /* Return 1 if there's a change in some property between BEG and END. */
986 property_change_between_p (EMACS_INT beg, EMACS_INT end)
988 register INTERVAL i, next;
989 Lisp_Object object, pos;
991 XSETBUFFER (object, current_buffer);
992 XSETFASTINT (pos, beg);
994 i = validate_interval_range (object, &pos, &pos, soft);
995 if (NULL_INTERVAL_P (i))
996 return 0;
998 next = next_interval (i);
999 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
1001 next = next_interval (next);
1002 if (NULL_INTERVAL_P (next))
1003 return 0;
1004 if (next->position >= end)
1005 return 0;
1008 if (NULL_INTERVAL_P (next))
1009 return 0;
1011 return 1;
1014 DEFUN ("next-single-property-change", Fnext_single_property_change,
1015 Snext_single_property_change, 2, 4, 0,
1016 doc: /* Return the position of next property change for a specific property.
1017 Scans characters forward from POSITION till it finds
1018 a change in the PROP property, then returns the position of the change.
1019 If the optional third argument OBJECT is a buffer (or nil, which means
1020 the current buffer), POSITION is a buffer position (integer or marker).
1021 If OBJECT is a string, POSITION is a 0-based index into it.
1022 The property values are compared with `eq'.
1023 Return nil if the property is constant all the way to the end of OBJECT.
1024 If the value is non-nil, it is a position greater than POSITION, never equal.
1026 If the optional fourth argument LIMIT is non-nil, don't search
1027 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1028 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1030 register INTERVAL i, next;
1031 register Lisp_Object here_val;
1033 if (NILP (object))
1034 XSETBUFFER (object, current_buffer);
1036 if (!NILP (limit))
1037 CHECK_NUMBER_COERCE_MARKER (limit);
1039 i = validate_interval_range (object, &position, &position, soft);
1040 if (NULL_INTERVAL_P (i))
1041 return limit;
1043 here_val = textget (i->plist, prop);
1044 next = next_interval (i);
1045 while (! NULL_INTERVAL_P (next)
1046 && EQ (here_val, textget (next->plist, prop))
1047 && (NILP (limit) || next->position < XFASTINT (limit)))
1048 next = next_interval (next);
1050 if (NULL_INTERVAL_P (next)
1051 || (next->position
1052 >= (INTEGERP (limit)
1053 ? XFASTINT (limit)
1054 : (STRINGP (object)
1055 ? SCHARS (object)
1056 : BUF_ZV (XBUFFER (object))))))
1057 return limit;
1058 else
1059 return make_number (next->position);
1062 DEFUN ("previous-property-change", Fprevious_property_change,
1063 Sprevious_property_change, 1, 3, 0,
1064 doc: /* Return the position of previous property change.
1065 Scans characters backwards from POSITION in OBJECT till it finds
1066 a change in some text property, then returns the position of the change.
1067 If the optional second argument OBJECT is a buffer (or nil, which means
1068 the current buffer), POSITION is a buffer position (integer or marker).
1069 If OBJECT is a string, POSITION is a 0-based index into it.
1070 Return nil if the property is constant all the way to the start of OBJECT.
1071 If the value is non-nil, it is a position less than POSITION, never equal.
1073 If the optional third argument LIMIT is non-nil, don't search
1074 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1075 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1077 register INTERVAL i, previous;
1079 if (NILP (object))
1080 XSETBUFFER (object, current_buffer);
1082 if (!NILP (limit))
1083 CHECK_NUMBER_COERCE_MARKER (limit);
1085 i = validate_interval_range (object, &position, &position, soft);
1086 if (NULL_INTERVAL_P (i))
1087 return limit;
1089 /* Start with the interval containing the char before point. */
1090 if (i->position == XFASTINT (position))
1091 i = previous_interval (i);
1093 previous = previous_interval (i);
1094 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1095 && (NILP (limit)
1096 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1097 previous = previous_interval (previous);
1099 if (NULL_INTERVAL_P (previous)
1100 || (previous->position + LENGTH (previous)
1101 <= (INTEGERP (limit)
1102 ? XFASTINT (limit)
1103 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1104 return limit;
1105 else
1106 return make_number (previous->position + LENGTH (previous));
1109 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1110 Sprevious_single_property_change, 2, 4, 0,
1111 doc: /* Return the position of previous property change for a specific property.
1112 Scans characters backward from POSITION till it finds
1113 a change in the PROP property, then returns the position of the change.
1114 If the optional third argument OBJECT is a buffer (or nil, which means
1115 the current buffer), POSITION is a buffer position (integer or marker).
1116 If OBJECT is a string, POSITION is a 0-based index into it.
1117 The property values are compared with `eq'.
1118 Return nil if the property is constant all the way to the start of OBJECT.
1119 If the value is non-nil, it is a position less than POSITION, never equal.
1121 If the optional fourth argument LIMIT is non-nil, don't search
1122 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1123 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1125 register INTERVAL i, previous;
1126 register Lisp_Object here_val;
1128 if (NILP (object))
1129 XSETBUFFER (object, current_buffer);
1131 if (!NILP (limit))
1132 CHECK_NUMBER_COERCE_MARKER (limit);
1134 i = validate_interval_range (object, &position, &position, soft);
1136 /* Start with the interval containing the char before point. */
1137 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1138 i = previous_interval (i);
1140 if (NULL_INTERVAL_P (i))
1141 return limit;
1143 here_val = textget (i->plist, prop);
1144 previous = previous_interval (i);
1145 while (!NULL_INTERVAL_P (previous)
1146 && EQ (here_val, textget (previous->plist, prop))
1147 && (NILP (limit)
1148 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1149 previous = previous_interval (previous);
1151 if (NULL_INTERVAL_P (previous)
1152 || (previous->position + LENGTH (previous)
1153 <= (INTEGERP (limit)
1154 ? XFASTINT (limit)
1155 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1156 return limit;
1157 else
1158 return make_number (previous->position + LENGTH (previous));
1161 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1163 DEFUN ("add-text-properties", Fadd_text_properties,
1164 Sadd_text_properties, 3, 4, 0,
1165 doc: /* Add properties to the text from START to END.
1166 The third argument PROPERTIES is a property list
1167 specifying the property values to add. If the optional fourth argument
1168 OBJECT is a buffer (or nil, which means the current buffer),
1169 START and END are buffer positions (integers or markers).
1170 If OBJECT is a string, START and END are 0-based indices into it.
1171 Return t if any property value actually changed, nil otherwise. */)
1172 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1174 register INTERVAL i, unchanged;
1175 register EMACS_INT s, len;
1176 register int modified = 0;
1177 struct gcpro gcpro1;
1179 properties = validate_plist (properties);
1180 if (NILP (properties))
1181 return Qnil;
1183 if (NILP (object))
1184 XSETBUFFER (object, current_buffer);
1186 i = validate_interval_range (object, &start, &end, hard);
1187 if (NULL_INTERVAL_P (i))
1188 return Qnil;
1190 s = XINT (start);
1191 len = XINT (end) - s;
1193 /* No need to protect OBJECT, because we GC only if it's a buffer,
1194 and live buffers are always protected. */
1195 GCPRO1 (properties);
1197 /* If we're not starting on an interval boundary, we have to
1198 split this interval. */
1199 if (i->position != s)
1201 /* If this interval already has the properties, we can
1202 skip it. */
1203 if (interval_has_all_properties (properties, i))
1205 EMACS_INT got = (LENGTH (i) - (s - i->position));
1206 if (got >= len)
1207 RETURN_UNGCPRO (Qnil);
1208 len -= got;
1209 i = next_interval (i);
1211 else
1213 unchanged = i;
1214 i = split_interval_right (unchanged, s - unchanged->position);
1215 copy_properties (unchanged, i);
1219 if (BUFFERP (object))
1220 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1222 /* We are at the beginning of interval I, with LEN chars to scan. */
1223 for (;;)
1225 if (i == 0)
1226 abort ();
1228 if (LENGTH (i) >= len)
1230 /* We can UNGCPRO safely here, because there will be just
1231 one more chance to gc, in the next call to add_properties,
1232 and after that we will not need PROPERTIES or OBJECT again. */
1233 UNGCPRO;
1235 if (interval_has_all_properties (properties, i))
1237 if (BUFFERP (object))
1238 signal_after_change (XINT (start), XINT (end) - XINT (start),
1239 XINT (end) - XINT (start));
1241 return modified ? Qt : Qnil;
1244 if (LENGTH (i) == len)
1246 add_properties (properties, i, object);
1247 if (BUFFERP (object))
1248 signal_after_change (XINT (start), XINT (end) - XINT (start),
1249 XINT (end) - XINT (start));
1250 return Qt;
1253 /* i doesn't have the properties, and goes past the change limit */
1254 unchanged = i;
1255 i = split_interval_left (unchanged, len);
1256 copy_properties (unchanged, i);
1257 add_properties (properties, i, object);
1258 if (BUFFERP (object))
1259 signal_after_change (XINT (start), XINT (end) - XINT (start),
1260 XINT (end) - XINT (start));
1261 return Qt;
1264 len -= LENGTH (i);
1265 modified += add_properties (properties, i, object);
1266 i = next_interval (i);
1270 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1272 DEFUN ("put-text-property", Fput_text_property,
1273 Sput_text_property, 4, 5, 0,
1274 doc: /* Set one property of the text from START to END.
1275 The third and fourth arguments PROPERTY and VALUE
1276 specify the property to add.
1277 If the optional fifth argument OBJECT is a buffer (or nil, which means
1278 the current buffer), START and END are buffer positions (integers or
1279 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1280 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1282 Fadd_text_properties (start, end,
1283 Fcons (property, Fcons (value, Qnil)),
1284 object);
1285 return Qnil;
1288 DEFUN ("set-text-properties", Fset_text_properties,
1289 Sset_text_properties, 3, 4, 0,
1290 doc: /* Completely replace properties of text from START to END.
1291 The third argument PROPERTIES is the new property list.
1292 If the optional fourth argument OBJECT is a buffer (or nil, which means
1293 the current buffer), START and END are buffer positions (integers or
1294 markers). If OBJECT is a string, START and END are 0-based indices into it.
1295 If PROPERTIES is nil, the effect is to remove all properties from
1296 the designated part of OBJECT. */)
1297 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1299 return set_text_properties (start, end, properties, object, Qt);
1303 /* Replace properties of text from START to END with new list of
1304 properties PROPERTIES. OBJECT is the buffer or string containing
1305 the text. OBJECT nil means use the current buffer.
1306 COHERENT_CHANGE_P nil means this is being called as an internal
1307 subroutine, rather than as a change primitive with checking of
1308 read-only, invoking change hooks, etc.. Value is nil if the
1309 function _detected_ that it did not replace any properties, non-nil
1310 otherwise. */
1312 Lisp_Object
1313 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, Lisp_Object coherent_change_p)
1315 register INTERVAL i;
1316 Lisp_Object ostart, oend;
1318 ostart = start;
1319 oend = end;
1321 properties = validate_plist (properties);
1323 if (NILP (object))
1324 XSETBUFFER (object, current_buffer);
1326 /* If we want no properties for a whole string,
1327 get rid of its intervals. */
1328 if (NILP (properties) && STRINGP (object)
1329 && XFASTINT (start) == 0
1330 && XFASTINT (end) == SCHARS (object))
1332 if (! STRING_INTERVALS (object))
1333 return Qnil;
1335 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1336 return Qt;
1339 i = validate_interval_range (object, &start, &end, soft);
1341 if (NULL_INTERVAL_P (i))
1343 /* If buffer has no properties, and we want none, return now. */
1344 if (NILP (properties))
1345 return Qnil;
1347 /* Restore the original START and END values
1348 because validate_interval_range increments them for strings. */
1349 start = ostart;
1350 end = oend;
1352 i = validate_interval_range (object, &start, &end, hard);
1353 /* This can return if start == end. */
1354 if (NULL_INTERVAL_P (i))
1355 return Qnil;
1358 if (BUFFERP (object) && !NILP (coherent_change_p))
1359 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1361 set_text_properties_1 (start, end, properties, object, i);
1363 if (BUFFERP (object) && !NILP (coherent_change_p))
1364 signal_after_change (XINT (start), XINT (end) - XINT (start),
1365 XINT (end) - XINT (start));
1366 return Qt;
1369 /* Replace properties of text from START to END with new list of
1370 properties PROPERTIES. BUFFER is the buffer containing
1371 the text. This does not obey any hooks.
1372 You can provide the interval that START is located in as I,
1373 or pass NULL for I and this function will find it.
1374 START and END can be in any order. */
1376 void
1377 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object buffer, INTERVAL i)
1379 register INTERVAL prev_changed = NULL_INTERVAL;
1380 register EMACS_INT s, len;
1381 INTERVAL unchanged;
1383 s = XINT (start);
1384 len = XINT (end) - s;
1385 if (len == 0)
1386 return;
1387 if (len < 0)
1389 s = s + len;
1390 len = - len;
1393 if (i == 0)
1394 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1396 if (i->position != s)
1398 unchanged = i;
1399 i = split_interval_right (unchanged, s - unchanged->position);
1401 if (LENGTH (i) > len)
1403 copy_properties (unchanged, i);
1404 i = split_interval_left (i, len);
1405 set_properties (properties, i, buffer);
1406 return;
1409 set_properties (properties, i, buffer);
1411 if (LENGTH (i) == len)
1412 return;
1414 prev_changed = i;
1415 len -= LENGTH (i);
1416 i = next_interval (i);
1419 /* We are starting at the beginning of an interval, I */
1420 while (len > 0)
1422 if (i == 0)
1423 abort ();
1425 if (LENGTH (i) >= len)
1427 if (LENGTH (i) > len)
1428 i = split_interval_left (i, len);
1430 /* We have to call set_properties even if we are going to
1431 merge the intervals, so as to make the undo records
1432 and cause redisplay to happen. */
1433 set_properties (properties, i, buffer);
1434 if (!NULL_INTERVAL_P (prev_changed))
1435 merge_interval_left (i);
1436 return;
1439 len -= LENGTH (i);
1441 /* We have to call set_properties even if we are going to
1442 merge the intervals, so as to make the undo records
1443 and cause redisplay to happen. */
1444 set_properties (properties, i, buffer);
1445 if (NULL_INTERVAL_P (prev_changed))
1446 prev_changed = i;
1447 else
1448 prev_changed = i = merge_interval_left (i);
1450 i = next_interval (i);
1454 DEFUN ("remove-text-properties", Fremove_text_properties,
1455 Sremove_text_properties, 3, 4, 0,
1456 doc: /* Remove some properties from text from START to END.
1457 The third argument PROPERTIES is a property list
1458 whose property names specify the properties to remove.
1459 \(The values stored in PROPERTIES are ignored.)
1460 If the optional fourth argument OBJECT is a buffer (or nil, which means
1461 the current buffer), START and END are buffer positions (integers or
1462 markers). If OBJECT is a string, START and END are 0-based indices into it.
1463 Return t if any property was actually removed, nil otherwise.
1465 Use `set-text-properties' if you want to remove all text properties. */)
1466 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1468 register INTERVAL i, unchanged;
1469 register EMACS_INT s, len;
1470 register int modified = 0;
1472 if (NILP (object))
1473 XSETBUFFER (object, current_buffer);
1475 i = validate_interval_range (object, &start, &end, soft);
1476 if (NULL_INTERVAL_P (i))
1477 return Qnil;
1479 s = XINT (start);
1480 len = XINT (end) - s;
1482 if (i->position != s)
1484 /* No properties on this first interval -- return if
1485 it covers the entire region. */
1486 if (! interval_has_some_properties (properties, i))
1488 EMACS_INT got = (LENGTH (i) - (s - i->position));
1489 if (got >= len)
1490 return Qnil;
1491 len -= got;
1492 i = next_interval (i);
1494 /* Split away the beginning of this interval; what we don't
1495 want to modify. */
1496 else
1498 unchanged = i;
1499 i = split_interval_right (unchanged, s - unchanged->position);
1500 copy_properties (unchanged, i);
1504 if (BUFFERP (object))
1505 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1507 /* We are at the beginning of an interval, with len to scan */
1508 for (;;)
1510 if (i == 0)
1511 abort ();
1513 if (LENGTH (i) >= len)
1515 if (! interval_has_some_properties (properties, i))
1516 return modified ? Qt : Qnil;
1518 if (LENGTH (i) == len)
1520 remove_properties (properties, Qnil, i, object);
1521 if (BUFFERP (object))
1522 signal_after_change (XINT (start), XINT (end) - XINT (start),
1523 XINT (end) - XINT (start));
1524 return Qt;
1527 /* i has the properties, and goes past the change limit */
1528 unchanged = i;
1529 i = split_interval_left (i, len);
1530 copy_properties (unchanged, i);
1531 remove_properties (properties, Qnil, i, object);
1532 if (BUFFERP (object))
1533 signal_after_change (XINT (start), XINT (end) - XINT (start),
1534 XINT (end) - XINT (start));
1535 return Qt;
1538 len -= LENGTH (i);
1539 modified += remove_properties (properties, Qnil, i, object);
1540 i = next_interval (i);
1544 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1545 Sremove_list_of_text_properties, 3, 4, 0,
1546 doc: /* Remove some properties from text from START to END.
1547 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1548 If the optional fourth argument OBJECT is a buffer (or nil, which means
1549 the current buffer), START and END are buffer positions (integers or
1550 markers). If OBJECT is a string, START and END are 0-based indices into it.
1551 Return t if any property was actually removed, nil otherwise. */)
1552 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1554 register INTERVAL i, unchanged;
1555 register EMACS_INT s, len;
1556 register int modified = 0;
1557 Lisp_Object properties;
1558 properties = list_of_properties;
1560 if (NILP (object))
1561 XSETBUFFER (object, current_buffer);
1563 i = validate_interval_range (object, &start, &end, soft);
1564 if (NULL_INTERVAL_P (i))
1565 return Qnil;
1567 s = XINT (start);
1568 len = XINT (end) - s;
1570 if (i->position != s)
1572 /* No properties on this first interval -- return if
1573 it covers the entire region. */
1574 if (! interval_has_some_properties_list (properties, i))
1576 EMACS_INT got = (LENGTH (i) - (s - i->position));
1577 if (got >= len)
1578 return Qnil;
1579 len -= got;
1580 i = next_interval (i);
1582 /* Split away the beginning of this interval; what we don't
1583 want to modify. */
1584 else
1586 unchanged = i;
1587 i = split_interval_right (unchanged, s - unchanged->position);
1588 copy_properties (unchanged, i);
1592 /* We are at the beginning of an interval, with len to scan.
1593 The flag `modified' records if changes have been made.
1594 When object is a buffer, we must call modify_region before changes are
1595 made and signal_after_change when we are done.
1596 We call modify_region before calling remove_properties if modified == 0,
1597 and we call signal_after_change before returning if modified != 0. */
1598 for (;;)
1600 if (i == 0)
1601 abort ();
1603 if (LENGTH (i) >= len)
1605 if (! interval_has_some_properties_list (properties, i))
1607 if (modified)
1609 if (BUFFERP (object))
1610 signal_after_change (XINT (start),
1611 XINT (end) - XINT (start),
1612 XINT (end) - XINT (start));
1613 return Qt;
1615 else
1616 return Qnil;
1618 else if (LENGTH (i) == len)
1620 if (!modified && BUFFERP (object))
1621 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1622 remove_properties (Qnil, properties, i, object);
1623 if (BUFFERP (object))
1624 signal_after_change (XINT (start), XINT (end) - XINT (start),
1625 XINT (end) - XINT (start));
1626 return Qt;
1628 else
1629 { /* i has the properties, and goes past the change limit. */
1630 unchanged = i;
1631 i = split_interval_left (i, len);
1632 copy_properties (unchanged, i);
1633 if (!modified && BUFFERP (object))
1634 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1635 remove_properties (Qnil, properties, i, object);
1636 if (BUFFERP (object))
1637 signal_after_change (XINT (start), XINT (end) - XINT (start),
1638 XINT (end) - XINT (start));
1639 return Qt;
1642 if (interval_has_some_properties_list (properties, i))
1644 if (!modified && BUFFERP (object))
1645 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1646 remove_properties (Qnil, properties, i, object);
1647 modified = 1;
1649 len -= LENGTH (i);
1650 i = next_interval (i);
1654 DEFUN ("text-property-any", Ftext_property_any,
1655 Stext_property_any, 4, 5, 0,
1656 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1657 If so, return the position of the first character whose property PROPERTY
1658 is `eq' to VALUE. Otherwise return nil.
1659 If the optional fifth argument OBJECT is a buffer (or nil, which means
1660 the current buffer), START and END are buffer positions (integers or
1661 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1662 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1664 register INTERVAL i;
1665 register EMACS_INT e, pos;
1667 if (NILP (object))
1668 XSETBUFFER (object, current_buffer);
1669 i = validate_interval_range (object, &start, &end, soft);
1670 if (NULL_INTERVAL_P (i))
1671 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1672 e = XINT (end);
1674 while (! NULL_INTERVAL_P (i))
1676 if (i->position >= e)
1677 break;
1678 if (EQ (textget (i->plist, property), value))
1680 pos = i->position;
1681 if (pos < XINT (start))
1682 pos = XINT (start);
1683 return make_number (pos);
1685 i = next_interval (i);
1687 return Qnil;
1690 DEFUN ("text-property-not-all", Ftext_property_not_all,
1691 Stext_property_not_all, 4, 5, 0,
1692 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1693 If so, return the position of the first character whose property PROPERTY
1694 is not `eq' to VALUE. Otherwise, return nil.
1695 If the optional fifth argument OBJECT is a buffer (or nil, which means
1696 the current buffer), START and END are buffer positions (integers or
1697 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1698 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1700 register INTERVAL i;
1701 register EMACS_INT s, e;
1703 if (NILP (object))
1704 XSETBUFFER (object, current_buffer);
1705 i = validate_interval_range (object, &start, &end, soft);
1706 if (NULL_INTERVAL_P (i))
1707 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1708 s = XINT (start);
1709 e = XINT (end);
1711 while (! NULL_INTERVAL_P (i))
1713 if (i->position >= e)
1714 break;
1715 if (! EQ (textget (i->plist, property), value))
1717 if (i->position > s)
1718 s = i->position;
1719 return make_number (s);
1721 i = next_interval (i);
1723 return Qnil;
1727 /* Return the direction from which the text-property PROP would be
1728 inherited by any new text inserted at POS: 1 if it would be
1729 inherited from the char after POS, -1 if it would be inherited from
1730 the char before POS, and 0 if from neither.
1731 BUFFER can be either a buffer or nil (meaning current buffer). */
1734 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1736 Lisp_Object prev_pos, front_sticky;
1737 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1739 if (NILP (buffer))
1740 XSETBUFFER (buffer, current_buffer);
1742 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1743 /* Consider previous character. */
1745 Lisp_Object rear_non_sticky;
1747 prev_pos = make_number (XINT (pos) - 1);
1748 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1750 if (!NILP (CONSP (rear_non_sticky)
1751 ? Fmemq (prop, rear_non_sticky)
1752 : rear_non_sticky))
1753 /* PROP is rear-non-sticky. */
1754 is_rear_sticky = 0;
1756 else
1757 return 0;
1759 /* Consider following character. */
1760 /* This signals an arg-out-of-range error if pos is outside the
1761 buffer's accessible range. */
1762 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1764 if (EQ (front_sticky, Qt)
1765 || (CONSP (front_sticky)
1766 && !NILP (Fmemq (prop, front_sticky))))
1767 /* PROP is inherited from after. */
1768 is_front_sticky = 1;
1770 /* Simple cases, where the properties are consistent. */
1771 if (is_rear_sticky && !is_front_sticky)
1772 return -1;
1773 else if (!is_rear_sticky && is_front_sticky)
1774 return 1;
1775 else if (!is_rear_sticky && !is_front_sticky)
1776 return 0;
1778 /* The stickiness properties are inconsistent, so we have to
1779 disambiguate. Basically, rear-sticky wins, _except_ if the
1780 property that would be inherited has a value of nil, in which case
1781 front-sticky wins. */
1782 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1783 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1784 return 1;
1785 else
1786 return -1;
1790 /* I don't think this is the right interface to export; how often do you
1791 want to do something like this, other than when you're copying objects
1792 around?
1794 I think it would be better to have a pair of functions, one which
1795 returns the text properties of a region as a list of ranges and
1796 plists, and another which applies such a list to another object. */
1798 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1799 SRC and DEST may each refer to strings or buffers.
1800 Optional sixth argument PROP causes only that property to be copied.
1801 Properties are copied to DEST as if by `add-text-properties'.
1802 Return t if any property value actually changed, nil otherwise. */
1804 /* Note this can GC when DEST is a buffer. */
1806 Lisp_Object
1807 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src, Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1809 INTERVAL i;
1810 Lisp_Object res;
1811 Lisp_Object stuff;
1812 Lisp_Object plist;
1813 EMACS_INT s, e, e2, p, len;
1814 int modified = 0;
1815 struct gcpro gcpro1, gcpro2;
1817 i = validate_interval_range (src, &start, &end, soft);
1818 if (NULL_INTERVAL_P (i))
1819 return Qnil;
1821 CHECK_NUMBER_COERCE_MARKER (pos);
1823 Lisp_Object dest_start, dest_end;
1825 dest_start = pos;
1826 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1827 /* Apply this to a copy of pos; it will try to increment its arguments,
1828 which we don't want. */
1829 validate_interval_range (dest, &dest_start, &dest_end, soft);
1832 s = XINT (start);
1833 e = XINT (end);
1834 p = XINT (pos);
1836 stuff = Qnil;
1838 while (s < e)
1840 e2 = i->position + LENGTH (i);
1841 if (e2 > e)
1842 e2 = e;
1843 len = e2 - s;
1845 plist = i->plist;
1846 if (! NILP (prop))
1847 while (! NILP (plist))
1849 if (EQ (Fcar (plist), prop))
1851 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1852 break;
1854 plist = Fcdr (Fcdr (plist));
1856 if (! NILP (plist))
1858 /* Must defer modifications to the interval tree in case src
1859 and dest refer to the same string or buffer. */
1860 stuff = Fcons (Fcons (make_number (p),
1861 Fcons (make_number (p + len),
1862 Fcons (plist, Qnil))),
1863 stuff);
1866 i = next_interval (i);
1867 if (NULL_INTERVAL_P (i))
1868 break;
1870 p += len;
1871 s = i->position;
1874 GCPRO2 (stuff, dest);
1876 while (! NILP (stuff))
1878 res = Fcar (stuff);
1879 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1880 Fcar (Fcdr (Fcdr (res))), dest);
1881 if (! NILP (res))
1882 modified++;
1883 stuff = Fcdr (stuff);
1886 UNGCPRO;
1888 return modified ? Qt : Qnil;
1892 /* Return a list representing the text properties of OBJECT between
1893 START and END. if PROP is non-nil, report only on that property.
1894 Each result list element has the form (S E PLIST), where S and E
1895 are positions in OBJECT and PLIST is a property list containing the
1896 text properties of OBJECT between S and E. Value is nil if OBJECT
1897 doesn't contain text properties between START and END. */
1899 Lisp_Object
1900 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1902 struct interval *i;
1903 Lisp_Object result;
1905 result = Qnil;
1907 i = validate_interval_range (object, &start, &end, soft);
1908 if (!NULL_INTERVAL_P (i))
1910 EMACS_INT s = XINT (start);
1911 EMACS_INT e = XINT (end);
1913 while (s < e)
1915 EMACS_INT interval_end, len;
1916 Lisp_Object plist;
1918 interval_end = i->position + LENGTH (i);
1919 if (interval_end > e)
1920 interval_end = e;
1921 len = interval_end - s;
1923 plist = i->plist;
1925 if (!NILP (prop))
1926 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1927 if (EQ (XCAR (plist), prop))
1929 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1930 break;
1933 if (!NILP (plist))
1934 result = Fcons (Fcons (make_number (s),
1935 Fcons (make_number (s + len),
1936 Fcons (plist, Qnil))),
1937 result);
1939 i = next_interval (i);
1940 if (NULL_INTERVAL_P (i))
1941 break;
1942 s = i->position;
1946 return result;
1950 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1951 (START END PLIST), where START and END are positions and PLIST is a
1952 property list containing the text properties to add. Adjust START
1953 and END positions by DELTA before adding properties. Value is
1954 non-zero if OBJECT was modified. */
1957 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
1959 struct gcpro gcpro1, gcpro2;
1960 int modified_p = 0;
1962 GCPRO2 (list, object);
1964 for (; CONSP (list); list = XCDR (list))
1966 Lisp_Object item, start, end, plist, tem;
1968 item = XCAR (list);
1969 start = make_number (XINT (XCAR (item)) + XINT (delta));
1970 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1971 plist = XCAR (XCDR (XCDR (item)));
1973 tem = Fadd_text_properties (start, end, plist, object);
1974 if (!NILP (tem))
1975 modified_p = 1;
1978 UNGCPRO;
1979 return modified_p;
1984 /* Modify end-points of ranges in LIST destructively, and return the
1985 new list. LIST is a list as returned from text_property_list.
1986 Discard properties that begin at or after NEW_END, and limit
1987 end-points to NEW_END. */
1989 Lisp_Object
1990 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
1992 Lisp_Object prev = Qnil, head = list;
1993 EMACS_INT max = XINT (new_end);
1995 for (; CONSP (list); prev = list, list = XCDR (list))
1997 Lisp_Object item, beg, end;
1999 item = XCAR (list);
2000 beg = XCAR (item);
2001 end = XCAR (XCDR (item));
2003 if (XINT (beg) >= max)
2005 /* The start-point is past the end of the new string.
2006 Discard this property. */
2007 if (EQ (head, list))
2008 head = XCDR (list);
2009 else
2010 XSETCDR (prev, XCDR (list));
2012 else if (XINT (end) > max)
2013 /* The end-point is past the end of the new string. */
2014 XSETCAR (XCDR (item), new_end);
2017 return head;
2022 /* Call the modification hook functions in LIST, each with START and END. */
2024 static void
2025 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2027 struct gcpro gcpro1;
2028 GCPRO1 (list);
2029 while (!NILP (list))
2031 call2 (Fcar (list), start, end);
2032 list = Fcdr (list);
2034 UNGCPRO;
2037 /* Check for read-only intervals between character positions START ... END,
2038 in BUF, and signal an error if we find one.
2040 Then check for any modification hooks in the range.
2041 Create a list of all these hooks in lexicographic order,
2042 eliminating consecutive extra copies of the same hook. Then call
2043 those hooks in order, with START and END - 1 as arguments. */
2045 void
2046 verify_interval_modification (struct buffer *buf, int start, int end)
2048 register INTERVAL intervals = BUF_INTERVALS (buf);
2049 register INTERVAL i;
2050 Lisp_Object hooks;
2051 register Lisp_Object prev_mod_hooks;
2052 Lisp_Object mod_hooks;
2053 struct gcpro gcpro1;
2055 hooks = Qnil;
2056 prev_mod_hooks = Qnil;
2057 mod_hooks = Qnil;
2059 interval_insert_behind_hooks = Qnil;
2060 interval_insert_in_front_hooks = Qnil;
2062 if (NULL_INTERVAL_P (intervals))
2063 return;
2065 if (start > end)
2067 EMACS_INT temp = start;
2068 start = end;
2069 end = temp;
2072 /* For an insert operation, check the two chars around the position. */
2073 if (start == end)
2075 INTERVAL prev = NULL;
2076 Lisp_Object before, after;
2078 /* Set I to the interval containing the char after START,
2079 and PREV to the interval containing the char before START.
2080 Either one may be null. They may be equal. */
2081 i = find_interval (intervals, start);
2083 if (start == BUF_BEGV (buf))
2084 prev = 0;
2085 else if (i->position == start)
2086 prev = previous_interval (i);
2087 else if (i->position < start)
2088 prev = i;
2089 if (start == BUF_ZV (buf))
2090 i = 0;
2092 /* If Vinhibit_read_only is set and is not a list, we can
2093 skip the read_only checks. */
2094 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2096 /* If I and PREV differ we need to check for the read-only
2097 property together with its stickiness. If either I or
2098 PREV are 0, this check is all we need.
2099 We have to take special care, since read-only may be
2100 indirectly defined via the category property. */
2101 if (i != prev)
2103 if (! NULL_INTERVAL_P (i))
2105 after = textget (i->plist, Qread_only);
2107 /* If interval I is read-only and read-only is
2108 front-sticky, inhibit insertion.
2109 Check for read-only as well as category. */
2110 if (! NILP (after)
2111 && NILP (Fmemq (after, Vinhibit_read_only)))
2113 Lisp_Object tem;
2115 tem = textget (i->plist, Qfront_sticky);
2116 if (TMEM (Qread_only, tem)
2117 || (NILP (Fplist_get (i->plist, Qread_only))
2118 && TMEM (Qcategory, tem)))
2119 text_read_only (after);
2123 if (! NULL_INTERVAL_P (prev))
2125 before = textget (prev->plist, Qread_only);
2127 /* If interval PREV is read-only and read-only isn't
2128 rear-nonsticky, inhibit insertion.
2129 Check for read-only as well as category. */
2130 if (! NILP (before)
2131 && NILP (Fmemq (before, Vinhibit_read_only)))
2133 Lisp_Object tem;
2135 tem = textget (prev->plist, Qrear_nonsticky);
2136 if (! TMEM (Qread_only, tem)
2137 && (! NILP (Fplist_get (prev->plist,Qread_only))
2138 || ! TMEM (Qcategory, tem)))
2139 text_read_only (before);
2143 else if (! NULL_INTERVAL_P (i))
2145 after = textget (i->plist, Qread_only);
2147 /* If interval I is read-only and read-only is
2148 front-sticky, inhibit insertion.
2149 Check for read-only as well as category. */
2150 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2152 Lisp_Object tem;
2154 tem = textget (i->plist, Qfront_sticky);
2155 if (TMEM (Qread_only, tem)
2156 || (NILP (Fplist_get (i->plist, Qread_only))
2157 && TMEM (Qcategory, tem)))
2158 text_read_only (after);
2160 tem = textget (prev->plist, Qrear_nonsticky);
2161 if (! TMEM (Qread_only, tem)
2162 && (! NILP (Fplist_get (prev->plist, Qread_only))
2163 || ! TMEM (Qcategory, tem)))
2164 text_read_only (after);
2169 /* Run both insert hooks (just once if they're the same). */
2170 if (!NULL_INTERVAL_P (prev))
2171 interval_insert_behind_hooks
2172 = textget (prev->plist, Qinsert_behind_hooks);
2173 if (!NULL_INTERVAL_P (i))
2174 interval_insert_in_front_hooks
2175 = textget (i->plist, Qinsert_in_front_hooks);
2177 else
2179 /* Loop over intervals on or next to START...END,
2180 collecting their hooks. */
2182 i = find_interval (intervals, start);
2185 if (! INTERVAL_WRITABLE_P (i))
2186 text_read_only (textget (i->plist, Qread_only));
2188 if (!inhibit_modification_hooks)
2190 mod_hooks = textget (i->plist, Qmodification_hooks);
2191 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2193 hooks = Fcons (mod_hooks, hooks);
2194 prev_mod_hooks = mod_hooks;
2198 i = next_interval (i);
2200 /* Keep going thru the interval containing the char before END. */
2201 while (! NULL_INTERVAL_P (i) && i->position < end);
2203 if (!inhibit_modification_hooks)
2205 GCPRO1 (hooks);
2206 hooks = Fnreverse (hooks);
2207 while (! EQ (hooks, Qnil))
2209 call_mod_hooks (Fcar (hooks), make_number (start),
2210 make_number (end));
2211 hooks = Fcdr (hooks);
2213 UNGCPRO;
2218 /* Run the interval hooks for an insertion on character range START ... END.
2219 verify_interval_modification chose which hooks to run;
2220 this function is called after the insertion happens
2221 so it can indicate the range of inserted text. */
2223 void
2224 report_interval_modification (Lisp_Object start, Lisp_Object end)
2226 if (! NILP (interval_insert_behind_hooks))
2227 call_mod_hooks (interval_insert_behind_hooks, start, end);
2228 if (! NILP (interval_insert_in_front_hooks)
2229 && ! EQ (interval_insert_in_front_hooks,
2230 interval_insert_behind_hooks))
2231 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2234 void
2235 syms_of_textprop (void)
2237 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2238 doc: /* Property-list used as default values.
2239 The value of a property in this list is seen as the value for every
2240 character that does not have its own value for that property. */);
2241 Vdefault_text_properties = Qnil;
2243 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2244 doc: /* Alist of alternative properties for properties without a value.
2245 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2246 If a piece of text has no direct value for a particular property, then
2247 this alist is consulted. If that property appears in the alist, then
2248 the first non-nil value from the associated alternative properties is
2249 returned. */);
2250 Vchar_property_alias_alist = Qnil;
2252 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2253 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2254 This also inhibits the use of the `intangible' text property. */);
2255 Vinhibit_point_motion_hooks = Qnil;
2257 DEFVAR_LISP ("text-property-default-nonsticky",
2258 &Vtext_property_default_nonsticky,
2259 doc: /* Alist of properties vs the corresponding non-stickinesses.
2260 Each element has the form (PROPERTY . NONSTICKINESS).
2262 If a character in a buffer has PROPERTY, new text inserted adjacent to
2263 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2264 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2265 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2266 /* Text property `syntax-table' should be nonsticky by default. */
2267 Vtext_property_default_nonsticky
2268 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt), Qnil);
2270 staticpro (&interval_insert_behind_hooks);
2271 staticpro (&interval_insert_in_front_hooks);
2272 interval_insert_behind_hooks = Qnil;
2273 interval_insert_in_front_hooks = Qnil;
2276 /* Common attributes one might give text */
2278 staticpro (&Qforeground);
2279 Qforeground = intern_c_string ("foreground");
2280 staticpro (&Qbackground);
2281 Qbackground = intern_c_string ("background");
2282 staticpro (&Qfont);
2283 Qfont = intern_c_string ("font");
2284 staticpro (&Qstipple);
2285 Qstipple = intern_c_string ("stipple");
2286 staticpro (&Qunderline);
2287 Qunderline = intern_c_string ("underline");
2288 staticpro (&Qread_only);
2289 Qread_only = intern_c_string ("read-only");
2290 staticpro (&Qinvisible);
2291 Qinvisible = intern_c_string ("invisible");
2292 staticpro (&Qintangible);
2293 Qintangible = intern_c_string ("intangible");
2294 staticpro (&Qcategory);
2295 Qcategory = intern_c_string ("category");
2296 staticpro (&Qlocal_map);
2297 Qlocal_map = intern_c_string ("local-map");
2298 staticpro (&Qfront_sticky);
2299 Qfront_sticky = intern_c_string ("front-sticky");
2300 staticpro (&Qrear_nonsticky);
2301 Qrear_nonsticky = intern_c_string ("rear-nonsticky");
2302 staticpro (&Qmouse_face);
2303 Qmouse_face = intern_c_string ("mouse-face");
2304 staticpro (&Qminibuffer_prompt);
2305 Qminibuffer_prompt = intern_c_string ("minibuffer-prompt");
2307 /* Properties that text might use to specify certain actions */
2309 staticpro (&Qmouse_left);
2310 Qmouse_left = intern_c_string ("mouse-left");
2311 staticpro (&Qmouse_entered);
2312 Qmouse_entered = intern_c_string ("mouse-entered");
2313 staticpro (&Qpoint_left);
2314 Qpoint_left = intern_c_string ("point-left");
2315 staticpro (&Qpoint_entered);
2316 Qpoint_entered = intern_c_string ("point-entered");
2318 defsubr (&Stext_properties_at);
2319 defsubr (&Sget_text_property);
2320 defsubr (&Sget_char_property);
2321 defsubr (&Sget_char_property_and_overlay);
2322 defsubr (&Snext_char_property_change);
2323 defsubr (&Sprevious_char_property_change);
2324 defsubr (&Snext_single_char_property_change);
2325 defsubr (&Sprevious_single_char_property_change);
2326 defsubr (&Snext_property_change);
2327 defsubr (&Snext_single_property_change);
2328 defsubr (&Sprevious_property_change);
2329 defsubr (&Sprevious_single_property_change);
2330 defsubr (&Sadd_text_properties);
2331 defsubr (&Sput_text_property);
2332 defsubr (&Sset_text_properties);
2333 defsubr (&Sremove_text_properties);
2334 defsubr (&Sremove_list_of_text_properties);
2335 defsubr (&Stext_property_any);
2336 defsubr (&Stext_property_not_all);
2337 /* defsubr (&Serase_text_properties); */
2338 /* defsubr (&Scopy_text_properties); */