* todo-mode.el: Offer to convert legacy file. Update commentary.
[emacs.git] / src / textprop.c
blobcc364d5a38cfd6683fe0968f04a739a03a774cbf
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2013 Free Software Foundation,
3 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>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "character.h"
25 #include "buffer.h"
26 #include "window.h"
28 /* Test for membership, allowing for t (actually any non-cons) to mean the
29 universal set. */
31 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
34 /* NOTES: previous- and next- property change will have to skip
35 zero-length intervals if they are implemented. This could be done
36 inside next_interval and previous_interval.
38 set_properties needs to deal with the interval property cache.
40 It is assumed that for any interval plist, a property appears
41 only once on the list. Although some code i.e., remove_properties,
42 handles the more general case, the uniqueness of properties is
43 necessary for the system to remain consistent. This requirement
44 is enforced by the subrs installing properties onto the intervals. */
47 /* Types of hooks. */
48 static Lisp_Object Qmouse_left;
49 static Lisp_Object Qmouse_entered;
50 Lisp_Object Qpoint_left;
51 Lisp_Object Qpoint_entered;
52 Lisp_Object Qcategory;
53 Lisp_Object Qlocal_map;
55 /* Visual properties text (including strings) may have. */
56 static Lisp_Object Qforeground, Qbackground, Qunderline;
57 Lisp_Object Qfont;
58 static Lisp_Object Qstipple;
59 Lisp_Object Qinvisible, Qintangible, Qmouse_face;
60 static Lisp_Object Qread_only;
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 /* verify_interval_modification saves insertion hooks here
72 to be run later by report_interval_modification. */
73 static Lisp_Object interval_insert_behind_hooks;
74 static Lisp_Object interval_insert_in_front_hooks;
77 /* Signal a `text-read-only' error. This function makes it easier
78 to capture that error in GDB by putting a breakpoint on it. */
80 static _Noreturn void
81 text_read_only (Lisp_Object propval)
83 if (STRINGP (propval))
84 xsignal1 (Qtext_read_only, propval);
86 xsignal0 (Qtext_read_only);
89 /* Prepare to modify the region of BUFFER from START to END. */
91 static void
92 modify_region (Lisp_Object buffer, Lisp_Object start, Lisp_Object end)
94 struct buffer *buf = XBUFFER (buffer), *old = current_buffer;
96 set_buffer_internal (buf);
97 modify_region_1 (XINT (start), XINT (end), true);
98 set_buffer_internal (old);
101 /* Extract the interval at the position pointed to by BEGIN from
102 OBJECT, a string or buffer. Additionally, check that the positions
103 pointed to by BEGIN and END are within the bounds of OBJECT, and
104 reverse them if *BEGIN is greater than *END. The objects pointed
105 to by BEGIN and END may be integers or markers; if the latter, they
106 are coerced to integers.
108 When OBJECT is a string, we increment *BEGIN and *END
109 to make them origin-one.
111 Note that buffer points don't correspond to interval indices.
112 For example, point-max is 1 greater than the index of the last
113 character. This difference is handled in the caller, which uses
114 the validated points to determine a length, and operates on that.
115 Exceptions are Ftext_properties_at, Fnext_property_change, and
116 Fprevious_property_change which call this function with BEGIN == END.
117 Handle this case specially.
119 If FORCE is soft (0), it's OK to return NULL. Otherwise,
120 create an interval tree for OBJECT if one doesn't exist, provided
121 the object actually contains text. In the current design, if there
122 is no text, there can be no text properties. */
124 #define soft 0
125 #define hard 1
127 INTERVAL
128 validate_interval_range (Lisp_Object object, Lisp_Object *begin,
129 Lisp_Object *end, bool force)
131 INTERVAL i;
132 ptrdiff_t searchpos;
134 CHECK_STRING_OR_BUFFER (object);
135 CHECK_NUMBER_COERCE_MARKER (*begin);
136 CHECK_NUMBER_COERCE_MARKER (*end);
138 /* If we are asked for a point, but from a subr which operates
139 on a range, then return nothing. */
140 if (EQ (*begin, *end) && begin != end)
141 return NULL;
143 if (XINT (*begin) > XINT (*end))
145 Lisp_Object n;
146 n = *begin;
147 *begin = *end;
148 *end = n;
151 if (BUFFERP (object))
153 register struct buffer *b = XBUFFER (object);
155 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
156 && XINT (*end) <= BUF_ZV (b)))
157 args_out_of_range (*begin, *end);
158 i = buffer_intervals (b);
160 /* If there's no text, there are no properties. */
161 if (BUF_BEGV (b) == BUF_ZV (b))
162 return NULL;
164 searchpos = XINT (*begin);
166 else
168 ptrdiff_t len = SCHARS (object);
170 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
171 && XINT (*end) <= len))
172 args_out_of_range (*begin, *end);
173 XSETFASTINT (*begin, XFASTINT (*begin));
174 if (begin != end)
175 XSETFASTINT (*end, XFASTINT (*end));
176 i = string_intervals (object);
178 if (len == 0)
179 return NULL;
181 searchpos = XINT (*begin);
184 if (!i)
185 return (force ? create_root_interval (object) : i);
187 return find_interval (i, searchpos);
190 /* Validate LIST as a property list. If LIST is not a list, then
191 make one consisting of (LIST nil). Otherwise, verify that LIST
192 is even numbered and thus suitable as a plist. */
194 static Lisp_Object
195 validate_plist (Lisp_Object list)
197 if (NILP (list))
198 return Qnil;
200 if (CONSP (list))
202 bool odd_length = 0;
203 Lisp_Object tail;
204 for (tail = list; CONSP (tail); tail = XCDR (tail))
206 odd_length ^= 1;
207 QUIT;
209 if (odd_length)
210 error ("Odd length text property list");
211 return list;
214 return Fcons (list, Fcons (Qnil, Qnil));
217 /* Return true if interval I has all the properties,
218 with the same values, of list PLIST. */
220 static bool
221 interval_has_all_properties (Lisp_Object plist, INTERVAL i)
223 Lisp_Object tail1, tail2;
225 /* Go through each element of PLIST. */
226 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
228 Lisp_Object sym1 = XCAR (tail1);
229 bool found = 0;
231 /* Go through I's plist, looking for sym1 */
232 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
233 if (EQ (sym1, XCAR (tail2)))
235 /* Found the same property on both lists. If the
236 values are unequal, return zero. */
237 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
238 return 0;
240 /* Property has same value on both lists; go to next one. */
241 found = 1;
242 break;
245 if (! found)
246 return 0;
249 return 1;
252 /* Return true if the plist of interval I has any of the
253 properties of PLIST, regardless of their values. */
255 static bool
256 interval_has_some_properties (Lisp_Object plist, INTERVAL i)
258 Lisp_Object tail1, tail2, sym;
260 /* Go through each element of PLIST. */
261 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
263 sym = XCAR (tail1);
265 /* Go through i's plist, looking for tail1 */
266 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
267 if (EQ (sym, XCAR (tail2)))
268 return 1;
271 return 0;
274 /* Return nonzero if the plist of interval I has any of the
275 property names in LIST, regardless of their values. */
277 static bool
278 interval_has_some_properties_list (Lisp_Object list, INTERVAL i)
280 Lisp_Object tail1, tail2, sym;
282 /* Go through each element of LIST. */
283 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
285 sym = XCAR (tail1);
287 /* Go through i's plist, looking for tail1 */
288 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
289 if (EQ (sym, XCAR (tail2)))
290 return 1;
293 return 0;
296 /* Changing the plists of individual intervals. */
298 /* Return the value of PROP in property-list PLIST, or Qunbound if it
299 has none. */
300 static Lisp_Object
301 property_value (Lisp_Object plist, Lisp_Object prop)
303 Lisp_Object value;
305 while (PLIST_ELT_P (plist, value))
306 if (EQ (XCAR (plist), prop))
307 return XCAR (value);
308 else
309 plist = XCDR (value);
311 return Qunbound;
314 /* Set the properties of INTERVAL to PROPERTIES,
315 and record undo info for the previous values.
316 OBJECT is the string or buffer that INTERVAL belongs to. */
318 static void
319 set_properties (Lisp_Object properties, INTERVAL interval, Lisp_Object object)
321 Lisp_Object sym, value;
323 if (BUFFERP (object))
325 /* For each property in the old plist which is missing from PROPERTIES,
326 or has a different value in PROPERTIES, make an undo record. */
327 for (sym = interval->plist;
328 PLIST_ELT_P (sym, value);
329 sym = XCDR (value))
330 if (! EQ (property_value (properties, XCAR (sym)),
331 XCAR (value)))
333 record_property_change (interval->position, LENGTH (interval),
334 XCAR (sym), XCAR (value),
335 object);
338 /* For each new property that has no value at all in the old plist,
339 make an undo record binding it to nil, so it will be removed. */
340 for (sym = properties;
341 PLIST_ELT_P (sym, value);
342 sym = XCDR (value))
343 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
345 record_property_change (interval->position, LENGTH (interval),
346 XCAR (sym), Qnil,
347 object);
351 /* Store new properties. */
352 set_interval_plist (interval, Fcopy_sequence (properties));
355 /* Add the properties of PLIST to the interval I, or set
356 the value of I's property to the value of the property on PLIST
357 if they are different.
359 OBJECT should be the string or buffer the interval is in.
361 Return true if this changes I (i.e., if any members of PLIST
362 are actually added to I's plist) */
364 static bool
365 add_properties (Lisp_Object plist, INTERVAL i, Lisp_Object object)
367 Lisp_Object tail1, tail2, sym1, val1;
368 bool changed = 0;
369 struct gcpro gcpro1, gcpro2, gcpro3;
371 tail1 = plist;
372 sym1 = Qnil;
373 val1 = Qnil;
374 /* No need to protect OBJECT, because we can GC only in the case
375 where it is a buffer, and live buffers are always protected.
376 I and its plist are also protected, via OBJECT. */
377 GCPRO3 (tail1, sym1, val1);
379 /* Go through each element of PLIST. */
380 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
382 bool found = 0;
383 sym1 = XCAR (tail1);
384 val1 = Fcar (XCDR (tail1));
386 /* Go through I's plist, looking for sym1 */
387 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
388 if (EQ (sym1, XCAR (tail2)))
390 /* No need to gcpro, because tail2 protects this
391 and it must be a cons cell (we get an error otherwise). */
392 register Lisp_Object this_cdr;
394 this_cdr = XCDR (tail2);
395 /* Found the property. Now check its value. */
396 found = 1;
398 /* The properties have the same value on both lists.
399 Continue to the next property. */
400 if (EQ (val1, Fcar (this_cdr)))
401 break;
403 /* Record this change in the buffer, for undo purposes. */
404 if (BUFFERP (object))
406 record_property_change (i->position, LENGTH (i),
407 sym1, Fcar (this_cdr), object);
410 /* I's property has a different value -- change it */
411 Fsetcar (this_cdr, val1);
412 changed = 1;
413 break;
416 if (! found)
418 /* Record this change in the buffer, for undo purposes. */
419 if (BUFFERP (object))
421 record_property_change (i->position, LENGTH (i),
422 sym1, Qnil, object);
424 set_interval_plist (i, Fcons (sym1, Fcons (val1, i->plist)));
425 changed = 1;
429 UNGCPRO;
431 return changed;
434 /* For any members of PLIST, or LIST,
435 which are properties of I, remove them from I's plist.
436 (If PLIST is non-nil, use that, otherwise use LIST.)
437 OBJECT is the string or buffer containing I. */
439 static bool
440 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
442 Lisp_Object tail1, tail2, sym, current_plist;
443 bool changed = 0;
445 /* True means tail1 is a plist, otherwise it is a list. */
446 bool use_plist;
448 current_plist = i->plist;
450 if (! NILP (plist))
451 tail1 = plist, use_plist = 1;
452 else
453 tail1 = list, use_plist = 0;
455 /* Go through each element of LIST or PLIST. */
456 while (CONSP (tail1))
458 sym = XCAR (tail1);
460 /* First, remove the symbol if it's at the head of the list */
461 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
463 if (BUFFERP (object))
464 record_property_change (i->position, LENGTH (i),
465 sym, XCAR (XCDR (current_plist)),
466 object);
468 current_plist = XCDR (XCDR (current_plist));
469 changed = 1;
472 /* Go through I's plist, looking for SYM. */
473 tail2 = current_plist;
474 while (! NILP (tail2))
476 register Lisp_Object this;
477 this = XCDR (XCDR (tail2));
478 if (CONSP (this) && EQ (sym, XCAR (this)))
480 if (BUFFERP (object))
481 record_property_change (i->position, LENGTH (i),
482 sym, XCAR (XCDR (this)), object);
484 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
485 changed = 1;
487 tail2 = this;
490 /* Advance thru TAIL1 one way or the other. */
491 tail1 = XCDR (tail1);
492 if (use_plist && CONSP (tail1))
493 tail1 = XCDR (tail1);
496 if (changed)
497 set_interval_plist (i, current_plist);
498 return changed;
501 /* Returns the interval of POSITION in OBJECT.
502 POSITION is BEG-based. */
504 INTERVAL
505 interval_of (ptrdiff_t position, Lisp_Object object)
507 register INTERVAL i;
508 ptrdiff_t beg, end;
510 if (NILP (object))
511 XSETBUFFER (object, current_buffer);
512 else if (EQ (object, Qt))
513 return NULL;
515 CHECK_STRING_OR_BUFFER (object);
517 if (BUFFERP (object))
519 register struct buffer *b = XBUFFER (object);
521 beg = BUF_BEGV (b);
522 end = BUF_ZV (b);
523 i = buffer_intervals (b);
525 else
527 beg = 0;
528 end = SCHARS (object);
529 i = string_intervals (object);
532 if (!(beg <= position && position <= end))
533 args_out_of_range (make_number (position), make_number (position));
534 if (beg == end || !i)
535 return NULL;
537 return find_interval (i, position);
540 DEFUN ("text-properties-at", Ftext_properties_at,
541 Stext_properties_at, 1, 2, 0,
542 doc: /* Return the list of properties of the character at POSITION in OBJECT.
543 If the optional second argument OBJECT is a buffer (or nil, which means
544 the current buffer), POSITION is a buffer position (integer or marker).
545 If OBJECT is a string, POSITION is a 0-based index into it.
546 If POSITION is at the end of OBJECT, the value is nil. */)
547 (Lisp_Object position, Lisp_Object object)
549 register INTERVAL i;
551 if (NILP (object))
552 XSETBUFFER (object, current_buffer);
554 i = validate_interval_range (object, &position, &position, soft);
555 if (!i)
556 return Qnil;
557 /* If POSITION is at the end of the interval,
558 it means it's the end of OBJECT.
559 There are no properties at the very end,
560 since no character follows. */
561 if (XINT (position) == LENGTH (i) + i->position)
562 return Qnil;
564 return i->plist;
567 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
568 doc: /* Return the value of POSITION's property PROP, in OBJECT.
569 OBJECT should be a buffer or a string; if omitted or nil, it defaults
570 to the current buffer.
571 If POSITION is at the end of OBJECT, the value is nil. */)
572 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
574 return textget (Ftext_properties_at (position, object), prop);
577 /* Return the value of char's property PROP, in OBJECT at POSITION.
578 OBJECT is optional and defaults to the current buffer.
579 If OVERLAY is non-0, then in the case that the returned property is from
580 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
581 returned in *OVERLAY.
582 If POSITION is at the end of OBJECT, the value is nil.
583 If OBJECT is a buffer, then overlay properties are considered as well as
584 text properties.
585 If OBJECT is a window, then that window's buffer is used, but
586 window-specific overlays are considered only if they are associated
587 with OBJECT. */
588 Lisp_Object
589 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
591 struct window *w = 0;
593 CHECK_NUMBER_COERCE_MARKER (position);
595 if (NILP (object))
596 XSETBUFFER (object, current_buffer);
598 if (WINDOWP (object))
600 CHECK_LIVE_WINDOW (object);
601 w = XWINDOW (object);
602 object = w->contents;
604 if (BUFFERP (object))
606 ptrdiff_t noverlays;
607 Lisp_Object *overlay_vec;
608 struct buffer *obuf = current_buffer;
610 if (XINT (position) < BUF_BEGV (XBUFFER (object))
611 || XINT (position) > BUF_ZV (XBUFFER (object)))
612 xsignal1 (Qargs_out_of_range, position);
614 set_buffer_temp (XBUFFER (object));
616 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
617 noverlays = sort_overlays (overlay_vec, noverlays, w);
619 set_buffer_temp (obuf);
621 /* Now check the overlays in order of decreasing priority. */
622 while (--noverlays >= 0)
624 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
625 if (!NILP (tem))
627 if (overlay)
628 /* Return the overlay we got the property from. */
629 *overlay = overlay_vec[noverlays];
630 return tem;
635 if (overlay)
636 /* Indicate that the return value is not from an overlay. */
637 *overlay = Qnil;
639 /* Not a buffer, or no appropriate overlay, so fall through to the
640 simpler case. */
641 return Fget_text_property (position, prop, object);
644 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
645 doc: /* Return the value of POSITION's property PROP, in OBJECT.
646 Both overlay properties and text properties are checked.
647 OBJECT is optional and defaults to the current buffer.
648 If POSITION is at the end of OBJECT, the value is nil.
649 If OBJECT is a buffer, then overlay properties are considered as well as
650 text properties.
651 If OBJECT is a window, then that window's buffer is used, but window-specific
652 overlays are considered only if they are associated with OBJECT. */)
653 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
655 return get_char_property_and_overlay (position, prop, object, 0);
658 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
659 Sget_char_property_and_overlay, 2, 3, 0,
660 doc: /* Like `get-char-property', but with extra overlay information.
661 The value is a cons cell. Its car is the return value of `get-char-property'
662 with the same arguments--that is, the value of POSITION's property
663 PROP in OBJECT. Its cdr is the overlay in which the property was
664 found, or nil, if it was found as a text property or not found at all.
666 OBJECT is optional and defaults to the current buffer. OBJECT may be
667 a string, a buffer or a window. For strings, the cdr of the return
668 value is always nil, since strings do not have overlays. If OBJECT is
669 a window, then that window's buffer is used, but window-specific
670 overlays are considered only if they are associated with OBJECT. If
671 POSITION is at the end of OBJECT, both car and cdr are nil. */)
672 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
674 Lisp_Object overlay;
675 Lisp_Object val
676 = get_char_property_and_overlay (position, prop, object, &overlay);
677 return Fcons (val, overlay);
681 DEFUN ("next-char-property-change", Fnext_char_property_change,
682 Snext_char_property_change, 1, 2, 0,
683 doc: /* Return the position of next text property or overlay change.
684 This scans characters forward in the current buffer from POSITION till
685 it finds a change in some text property, or the beginning or end of an
686 overlay, and returns the position of that.
687 If none is found up to (point-max), the function returns (point-max).
689 If the optional second argument LIMIT is non-nil, don't search
690 past position LIMIT; return LIMIT if nothing is found before LIMIT.
691 LIMIT is a no-op if it is greater than (point-max). */)
692 (Lisp_Object position, Lisp_Object limit)
694 Lisp_Object temp;
696 temp = Fnext_overlay_change (position);
697 if (! NILP (limit))
699 CHECK_NUMBER_COERCE_MARKER (limit);
700 if (XINT (limit) < XINT (temp))
701 temp = limit;
703 return Fnext_property_change (position, Qnil, temp);
706 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
707 Sprevious_char_property_change, 1, 2, 0,
708 doc: /* Return the position of previous text property or overlay change.
709 Scans characters backward in the current buffer from POSITION till it
710 finds a change in some text property, or the beginning or end of an
711 overlay, and returns the position of that.
712 If none is found since (point-min), the function returns (point-min).
714 If the optional second argument LIMIT is non-nil, don't search
715 past position LIMIT; return LIMIT if nothing is found before LIMIT.
716 LIMIT is a no-op if it is less than (point-min). */)
717 (Lisp_Object position, Lisp_Object limit)
719 Lisp_Object temp;
721 temp = Fprevious_overlay_change (position);
722 if (! NILP (limit))
724 CHECK_NUMBER_COERCE_MARKER (limit);
725 if (XINT (limit) > XINT (temp))
726 temp = limit;
728 return Fprevious_property_change (position, Qnil, temp);
732 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
733 Snext_single_char_property_change, 2, 4, 0,
734 doc: /* Return the position of next text property or overlay change for a specific property.
735 Scans characters forward from POSITION till it finds
736 a change in the PROP property, then returns the position of the change.
737 If the optional third argument OBJECT is a buffer (or nil, which means
738 the current buffer), POSITION is a buffer position (integer or marker).
739 If OBJECT is a string, POSITION is a 0-based index into it.
741 In a string, scan runs to the end of the string.
742 In a buffer, it runs to (point-max), and the value cannot exceed that.
744 The property values are compared with `eq'.
745 If the property is constant all the way to the end of OBJECT, return the
746 last valid position in OBJECT.
747 If the optional fourth argument LIMIT is non-nil, don't search
748 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
749 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
751 if (STRINGP (object))
753 position = Fnext_single_property_change (position, prop, object, limit);
754 if (NILP (position))
756 if (NILP (limit))
757 position = make_number (SCHARS (object));
758 else
760 CHECK_NUMBER (limit);
761 position = limit;
765 else
767 Lisp_Object initial_value, value;
768 ptrdiff_t count = SPECPDL_INDEX ();
770 if (! NILP (object))
771 CHECK_BUFFER (object);
773 if (BUFFERP (object) && current_buffer != XBUFFER (object))
775 record_unwind_current_buffer ();
776 Fset_buffer (object);
779 CHECK_NUMBER_COERCE_MARKER (position);
781 initial_value = Fget_char_property (position, prop, object);
783 if (NILP (limit))
784 XSETFASTINT (limit, ZV);
785 else
786 CHECK_NUMBER_COERCE_MARKER (limit);
788 if (XFASTINT (position) >= XFASTINT (limit))
790 position = limit;
791 if (XFASTINT (position) > ZV)
792 XSETFASTINT (position, ZV);
794 else
795 while (1)
797 position = Fnext_char_property_change (position, limit);
798 if (XFASTINT (position) >= XFASTINT (limit))
800 position = limit;
801 break;
804 value = Fget_char_property (position, prop, object);
805 if (!EQ (value, initial_value))
806 break;
809 unbind_to (count, Qnil);
812 return position;
815 DEFUN ("previous-single-char-property-change",
816 Fprevious_single_char_property_change,
817 Sprevious_single_char_property_change, 2, 4, 0,
818 doc: /* Return the position of previous text property or overlay change for a specific property.
819 Scans characters backward from POSITION till it finds
820 a change in the PROP property, then returns the position of the change.
821 If the optional third argument OBJECT is a buffer (or nil, which means
822 the current buffer), POSITION is a buffer position (integer or marker).
823 If OBJECT is a string, POSITION is a 0-based index into it.
825 In a string, scan runs to the start of the string.
826 In a buffer, it runs to (point-min), and the value cannot be less than that.
828 The property values are compared with `eq'.
829 If the property is constant all the way to the start of OBJECT, return the
830 first valid position in OBJECT.
831 If the optional fourth argument LIMIT is non-nil, don't search back past
832 position LIMIT; return LIMIT if nothing is found before reaching LIMIT. */)
833 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
835 if (STRINGP (object))
837 position = Fprevious_single_property_change (position, prop, object, limit);
838 if (NILP (position))
840 if (NILP (limit))
841 position = make_number (0);
842 else
844 CHECK_NUMBER (limit);
845 position = limit;
849 else
851 ptrdiff_t count = SPECPDL_INDEX ();
853 if (! NILP (object))
854 CHECK_BUFFER (object);
856 if (BUFFERP (object) && current_buffer != XBUFFER (object))
858 record_unwind_current_buffer ();
859 Fset_buffer (object);
862 CHECK_NUMBER_COERCE_MARKER (position);
864 if (NILP (limit))
865 XSETFASTINT (limit, BEGV);
866 else
867 CHECK_NUMBER_COERCE_MARKER (limit);
869 if (XFASTINT (position) <= XFASTINT (limit))
871 position = limit;
872 if (XFASTINT (position) < BEGV)
873 XSETFASTINT (position, BEGV);
875 else
877 Lisp_Object initial_value
878 = Fget_char_property (make_number (XFASTINT (position) - 1),
879 prop, object);
881 while (1)
883 position = Fprevious_char_property_change (position, limit);
885 if (XFASTINT (position) <= XFASTINT (limit))
887 position = limit;
888 break;
890 else
892 Lisp_Object value
893 = Fget_char_property (make_number (XFASTINT (position) - 1),
894 prop, object);
896 if (!EQ (value, initial_value))
897 break;
902 unbind_to (count, Qnil);
905 return position;
908 DEFUN ("next-property-change", Fnext_property_change,
909 Snext_property_change, 1, 3, 0,
910 doc: /* Return the position of next property change.
911 Scans characters forward from POSITION in OBJECT till it finds
912 a change in some text property, then returns the position of the change.
913 If the optional second argument OBJECT is a buffer (or nil, which means
914 the current buffer), POSITION is a buffer position (integer or marker).
915 If OBJECT is a string, POSITION is a 0-based index into it.
916 Return nil if the property is constant all the way to the end of OBJECT.
917 If the value is non-nil, it is a position greater than POSITION, never equal.
919 If the optional third argument LIMIT is non-nil, don't search
920 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
921 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
923 register INTERVAL i, next;
925 if (NILP (object))
926 XSETBUFFER (object, current_buffer);
928 if (!NILP (limit) && !EQ (limit, Qt))
929 CHECK_NUMBER_COERCE_MARKER (limit);
931 i = validate_interval_range (object, &position, &position, soft);
933 /* If LIMIT is t, return start of next interval--don't
934 bother checking further intervals. */
935 if (EQ (limit, Qt))
937 if (!i)
938 next = i;
939 else
940 next = next_interval (i);
942 if (!next)
943 XSETFASTINT (position, (STRINGP (object)
944 ? SCHARS (object)
945 : BUF_ZV (XBUFFER (object))));
946 else
947 XSETFASTINT (position, next->position);
948 return position;
951 if (!i)
952 return limit;
954 next = next_interval (i);
956 while (next && intervals_equal (i, next)
957 && (NILP (limit) || next->position < XFASTINT (limit)))
958 next = next_interval (next);
960 if (!next
961 || (next->position
962 >= (INTEGERP (limit)
963 ? XFASTINT (limit)
964 : (STRINGP (object)
965 ? SCHARS (object)
966 : BUF_ZV (XBUFFER (object))))))
967 return limit;
968 else
969 return make_number (next->position);
972 DEFUN ("next-single-property-change", Fnext_single_property_change,
973 Snext_single_property_change, 2, 4, 0,
974 doc: /* Return the position of next property change for a specific property.
975 Scans characters forward from POSITION till it finds
976 a change in the PROP property, then returns the position of the change.
977 If the optional third argument OBJECT is a buffer (or nil, which means
978 the current buffer), POSITION is a buffer position (integer or marker).
979 If OBJECT is a string, POSITION is a 0-based index into it.
980 The property values are compared with `eq'.
981 Return nil if the property is constant all the way to the end of OBJECT.
982 If the value is non-nil, it is a position greater than POSITION, never equal.
984 If the optional fourth argument LIMIT is non-nil, don't search
985 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
986 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
988 register INTERVAL i, next;
989 register Lisp_Object here_val;
991 if (NILP (object))
992 XSETBUFFER (object, current_buffer);
994 if (!NILP (limit))
995 CHECK_NUMBER_COERCE_MARKER (limit);
997 i = validate_interval_range (object, &position, &position, soft);
998 if (!i)
999 return limit;
1001 here_val = textget (i->plist, prop);
1002 next = next_interval (i);
1003 while (next
1004 && EQ (here_val, textget (next->plist, prop))
1005 && (NILP (limit) || next->position < XFASTINT (limit)))
1006 next = next_interval (next);
1008 if (!next
1009 || (next->position
1010 >= (INTEGERP (limit)
1011 ? XFASTINT (limit)
1012 : (STRINGP (object)
1013 ? SCHARS (object)
1014 : BUF_ZV (XBUFFER (object))))))
1015 return limit;
1016 else
1017 return make_number (next->position);
1020 DEFUN ("previous-property-change", Fprevious_property_change,
1021 Sprevious_property_change, 1, 3, 0,
1022 doc: /* Return the position of previous property change.
1023 Scans characters backwards from POSITION in OBJECT till it finds
1024 a change in some text property, then returns the position of the change.
1025 If the optional second argument OBJECT is a buffer (or nil, which means
1026 the current buffer), POSITION is a buffer position (integer or marker).
1027 If OBJECT is a string, POSITION is a 0-based index into it.
1028 Return nil if the property is constant all the way to the start of OBJECT.
1029 If the value is non-nil, it is a position less than POSITION, never equal.
1031 If the optional third argument LIMIT is non-nil, don't search
1032 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1033 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1035 register INTERVAL i, previous;
1037 if (NILP (object))
1038 XSETBUFFER (object, current_buffer);
1040 if (!NILP (limit))
1041 CHECK_NUMBER_COERCE_MARKER (limit);
1043 i = validate_interval_range (object, &position, &position, soft);
1044 if (!i)
1045 return limit;
1047 /* Start with the interval containing the char before point. */
1048 if (i->position == XFASTINT (position))
1049 i = previous_interval (i);
1051 previous = previous_interval (i);
1052 while (previous && intervals_equal (previous, i)
1053 && (NILP (limit)
1054 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1055 previous = previous_interval (previous);
1057 if (!previous
1058 || (previous->position + LENGTH (previous)
1059 <= (INTEGERP (limit)
1060 ? XFASTINT (limit)
1061 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1062 return limit;
1063 else
1064 return make_number (previous->position + LENGTH (previous));
1067 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1068 Sprevious_single_property_change, 2, 4, 0,
1069 doc: /* Return the position of previous property change for a specific property.
1070 Scans characters backward from POSITION till it finds
1071 a change in the PROP property, then returns the position of the change.
1072 If the optional third argument OBJECT is a buffer (or nil, which means
1073 the current buffer), POSITION is a buffer position (integer or marker).
1074 If OBJECT is a string, POSITION is a 0-based index into it.
1075 The property values are compared with `eq'.
1076 Return nil if the property is constant all the way to the start of OBJECT.
1077 If the value is non-nil, it is a position less than POSITION, never equal.
1079 If the optional fourth argument LIMIT is non-nil, don't search
1080 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1081 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1083 register INTERVAL i, previous;
1084 register Lisp_Object here_val;
1086 if (NILP (object))
1087 XSETBUFFER (object, current_buffer);
1089 if (!NILP (limit))
1090 CHECK_NUMBER_COERCE_MARKER (limit);
1092 i = validate_interval_range (object, &position, &position, soft);
1094 /* Start with the interval containing the char before point. */
1095 if (i && i->position == XFASTINT (position))
1096 i = previous_interval (i);
1098 if (!i)
1099 return limit;
1101 here_val = textget (i->plist, prop);
1102 previous = previous_interval (i);
1103 while (previous
1104 && EQ (here_val, textget (previous->plist, prop))
1105 && (NILP (limit)
1106 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1107 previous = previous_interval (previous);
1109 if (!previous
1110 || (previous->position + LENGTH (previous)
1111 <= (INTEGERP (limit)
1112 ? XFASTINT (limit)
1113 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1114 return limit;
1115 else
1116 return make_number (previous->position + LENGTH (previous));
1119 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1121 DEFUN ("add-text-properties", Fadd_text_properties,
1122 Sadd_text_properties, 3, 4, 0,
1123 doc: /* Add properties to the text from START to END.
1124 The third argument PROPERTIES is a property list
1125 specifying the property values to add. If the optional fourth argument
1126 OBJECT is a buffer (or nil, which means the current buffer),
1127 START and END are buffer positions (integers or markers).
1128 If OBJECT is a string, START and END are 0-based indices into it.
1129 Return t if any property value actually changed, nil otherwise. */)
1130 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1132 INTERVAL i, unchanged;
1133 ptrdiff_t s, len;
1134 bool modified = 0;
1135 struct gcpro gcpro1;
1136 bool first_time = 1;
1138 properties = validate_plist (properties);
1139 if (NILP (properties))
1140 return Qnil;
1142 if (NILP (object))
1143 XSETBUFFER (object, current_buffer);
1145 retry:
1146 i = validate_interval_range (object, &start, &end, hard);
1147 if (!i)
1148 return Qnil;
1150 s = XINT (start);
1151 len = XINT (end) - s;
1153 /* No need to protect OBJECT, because we GC only if it's a buffer,
1154 and live buffers are always protected. */
1155 GCPRO1 (properties);
1157 /* If this interval already has the properties, we can skip it. */
1158 if (interval_has_all_properties (properties, i))
1160 ptrdiff_t got = LENGTH (i) - (s - i->position);
1164 if (got >= len)
1165 RETURN_UNGCPRO (Qnil);
1166 len -= got;
1167 i = next_interval (i);
1168 got = LENGTH (i);
1170 while (interval_has_all_properties (properties, i));
1172 else if (i->position != s)
1174 /* If we're not starting on an interval boundary, we have to
1175 split this interval. */
1176 unchanged = i;
1177 i = split_interval_right (unchanged, s - unchanged->position);
1178 copy_properties (unchanged, i);
1181 if (BUFFERP (object) && first_time)
1183 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1184 ptrdiff_t prev_pos = i->position;
1186 modify_region (object, start, end);
1187 /* If someone called us recursively as a side effect of
1188 modify_region, and changed the intervals behind our back
1189 (could happen if lock_file, called by prepare_to_modify_buffer,
1190 triggers redisplay, and that calls add-text-properties again
1191 in the same buffer), we cannot continue with I, because its
1192 data changed. So we restart the interval analysis anew. */
1193 if (TOTAL_LENGTH (i) != prev_total_length
1194 || i->position != prev_pos)
1196 first_time = 0;
1197 goto retry;
1201 /* We are at the beginning of interval I, with LEN chars to scan. */
1202 for (;;)
1204 eassert (i != 0);
1206 if (LENGTH (i) >= len)
1208 /* We can UNGCPRO safely here, because there will be just
1209 one more chance to gc, in the next call to add_properties,
1210 and after that we will not need PROPERTIES or OBJECT again. */
1211 UNGCPRO;
1213 if (interval_has_all_properties (properties, i))
1215 if (BUFFERP (object))
1216 signal_after_change (XINT (start), XINT (end) - XINT (start),
1217 XINT (end) - XINT (start));
1219 eassert (modified);
1220 return Qt;
1223 if (LENGTH (i) == len)
1225 add_properties (properties, i, object);
1226 if (BUFFERP (object))
1227 signal_after_change (XINT (start), XINT (end) - XINT (start),
1228 XINT (end) - XINT (start));
1229 return Qt;
1232 /* i doesn't have the properties, and goes past the change limit */
1233 unchanged = i;
1234 i = split_interval_left (unchanged, len);
1235 copy_properties (unchanged, i);
1236 add_properties (properties, i, object);
1237 if (BUFFERP (object))
1238 signal_after_change (XINT (start), XINT (end) - XINT (start),
1239 XINT (end) - XINT (start));
1240 return Qt;
1243 len -= LENGTH (i);
1244 modified |= add_properties (properties, i, object);
1245 i = next_interval (i);
1249 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1251 DEFUN ("put-text-property", Fput_text_property,
1252 Sput_text_property, 4, 5, 0,
1253 doc: /* Set one property of the text from START to END.
1254 The third and fourth arguments PROPERTY and VALUE
1255 specify the property to add.
1256 If the optional fifth argument OBJECT is a buffer (or nil, which means
1257 the current buffer), START and END are buffer positions (integers or
1258 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1259 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1261 Fadd_text_properties (start, end,
1262 Fcons (property, Fcons (value, Qnil)),
1263 object);
1264 return Qnil;
1267 DEFUN ("set-text-properties", Fset_text_properties,
1268 Sset_text_properties, 3, 4, 0,
1269 doc: /* Completely replace properties of text from START to END.
1270 The third argument PROPERTIES is the new property list.
1271 If the optional fourth argument OBJECT is a buffer (or nil, which means
1272 the current buffer), START and END are buffer positions (integers or
1273 markers). If OBJECT is a string, START and END are 0-based indices into it.
1274 If PROPERTIES is nil, the effect is to remove all properties from
1275 the designated part of OBJECT. */)
1276 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1278 return set_text_properties (start, end, properties, object, Qt);
1282 /* Replace properties of text from START to END with new list of
1283 properties PROPERTIES. OBJECT is the buffer or string containing
1284 the text. OBJECT nil means use the current buffer.
1285 COHERENT_CHANGE_P nil means this is being called as an internal
1286 subroutine, rather than as a change primitive with checking of
1287 read-only, invoking change hooks, etc.. Value is nil if the
1288 function _detected_ that it did not replace any properties, non-nil
1289 otherwise. */
1291 Lisp_Object
1292 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, Lisp_Object coherent_change_p)
1294 register INTERVAL i;
1295 Lisp_Object ostart, oend;
1297 ostart = start;
1298 oend = end;
1300 properties = validate_plist (properties);
1302 if (NILP (object))
1303 XSETBUFFER (object, current_buffer);
1305 /* If we want no properties for a whole string,
1306 get rid of its intervals. */
1307 if (NILP (properties) && STRINGP (object)
1308 && XFASTINT (start) == 0
1309 && XFASTINT (end) == SCHARS (object))
1311 if (!string_intervals (object))
1312 return Qnil;
1314 set_string_intervals (object, NULL);
1315 return Qt;
1318 i = validate_interval_range (object, &start, &end, soft);
1320 if (!i)
1322 /* If buffer has no properties, and we want none, return now. */
1323 if (NILP (properties))
1324 return Qnil;
1326 /* Restore the original START and END values
1327 because validate_interval_range increments them for strings. */
1328 start = ostart;
1329 end = oend;
1331 i = validate_interval_range (object, &start, &end, hard);
1332 /* This can return if start == end. */
1333 if (!i)
1334 return Qnil;
1337 if (BUFFERP (object) && !NILP (coherent_change_p))
1338 modify_region (object, start, end);
1340 set_text_properties_1 (start, end, properties, object, i);
1342 if (BUFFERP (object) && !NILP (coherent_change_p))
1343 signal_after_change (XINT (start), XINT (end) - XINT (start),
1344 XINT (end) - XINT (start));
1345 return Qt;
1348 /* Replace properties of text from START to END with new list of
1349 properties PROPERTIES. OBJECT is the buffer or string containing
1350 the text. This does not obey any hooks.
1351 You should provide the interval that START is located in as I.
1352 START and END can be in any order. */
1354 void
1355 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, INTERVAL i)
1357 register INTERVAL prev_changed = NULL;
1358 register ptrdiff_t s, len;
1359 INTERVAL unchanged;
1361 if (XINT (start) < XINT (end))
1363 s = XINT (start);
1364 len = XINT (end) - s;
1366 else if (XINT (end) < XINT (start))
1368 s = XINT (end);
1369 len = XINT (start) - s;
1371 else
1372 return;
1374 eassert (i);
1376 if (i->position != s)
1378 unchanged = i;
1379 i = split_interval_right (unchanged, s - unchanged->position);
1381 if (LENGTH (i) > len)
1383 copy_properties (unchanged, i);
1384 i = split_interval_left (i, len);
1385 set_properties (properties, i, object);
1386 return;
1389 set_properties (properties, i, object);
1391 if (LENGTH (i) == len)
1392 return;
1394 prev_changed = i;
1395 len -= LENGTH (i);
1396 i = next_interval (i);
1399 /* We are starting at the beginning of an interval I. LEN is positive. */
1402 eassert (i != 0);
1404 if (LENGTH (i) >= len)
1406 if (LENGTH (i) > len)
1407 i = split_interval_left (i, len);
1409 /* We have to call set_properties even if we are going to
1410 merge the intervals, so as to make the undo records
1411 and cause redisplay to happen. */
1412 set_properties (properties, i, object);
1413 if (prev_changed)
1414 merge_interval_left (i);
1415 return;
1418 len -= LENGTH (i);
1420 /* We have to call set_properties even if we are going to
1421 merge the intervals, so as to make the undo records
1422 and cause redisplay to happen. */
1423 set_properties (properties, i, object);
1424 if (!prev_changed)
1425 prev_changed = i;
1426 else
1427 prev_changed = i = merge_interval_left (i);
1429 i = next_interval (i);
1431 while (len > 0);
1434 DEFUN ("remove-text-properties", Fremove_text_properties,
1435 Sremove_text_properties, 3, 4, 0,
1436 doc: /* Remove some properties from text from START to END.
1437 The third argument PROPERTIES is a property list
1438 whose property names specify the properties to remove.
1439 \(The values stored in PROPERTIES are ignored.)
1440 If the optional fourth argument OBJECT is a buffer (or nil, which means
1441 the current buffer), START and END are buffer positions (integers or
1442 markers). If OBJECT is a string, START and END are 0-based indices into it.
1443 Return t if any property was actually removed, nil otherwise.
1445 Use `set-text-properties' if you want to remove all text properties. */)
1446 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1448 INTERVAL i, unchanged;
1449 ptrdiff_t s, len;
1450 bool modified = 0;
1451 bool first_time = 1;
1453 if (NILP (object))
1454 XSETBUFFER (object, current_buffer);
1456 retry:
1457 i = validate_interval_range (object, &start, &end, soft);
1458 if (!i)
1459 return Qnil;
1461 s = XINT (start);
1462 len = XINT (end) - s;
1464 /* If there are no properties on this entire interval, return. */
1465 if (! interval_has_some_properties (properties, i))
1467 ptrdiff_t got = LENGTH (i) - (s - i->position);
1471 if (got >= len)
1472 return Qnil;
1473 len -= got;
1474 i = next_interval (i);
1475 got = LENGTH (i);
1477 while (! interval_has_some_properties (properties, i));
1479 /* Split away the beginning of this interval; what we don't
1480 want to modify. */
1481 else if (i->position != s)
1483 unchanged = i;
1484 i = split_interval_right (unchanged, s - unchanged->position);
1485 copy_properties (unchanged, i);
1488 if (BUFFERP (object) && first_time)
1490 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1491 ptrdiff_t prev_pos = i->position;
1493 modify_region (object, start, end);
1494 /* If someone called us recursively as a side effect of
1495 modify_region, and changed the intervals behind our back
1496 (could happen if lock_file, called by prepare_to_modify_buffer,
1497 triggers redisplay, and that calls add-text-properties again
1498 in the same buffer), we cannot continue with I, because its
1499 data changed. So we restart the interval analysis anew. */
1500 if (TOTAL_LENGTH (i) != prev_total_length
1501 || i->position != prev_pos)
1503 first_time = 0;
1504 goto retry;
1508 /* We are at the beginning of an interval, with len to scan */
1509 for (;;)
1511 eassert (i != 0);
1513 if (LENGTH (i) >= len)
1515 if (! interval_has_some_properties (properties, i))
1517 eassert (modified);
1518 if (BUFFERP (object))
1519 signal_after_change (XINT (start), XINT (end) - XINT (start),
1520 XINT (end) - XINT (start));
1521 return Qt;
1524 if (LENGTH (i) == len)
1526 remove_properties (properties, Qnil, i, object);
1527 if (BUFFERP (object))
1528 signal_after_change (XINT (start), XINT (end) - XINT (start),
1529 XINT (end) - XINT (start));
1530 return Qt;
1533 /* i has the properties, and goes past the change limit */
1534 unchanged = i;
1535 i = split_interval_left (i, len);
1536 copy_properties (unchanged, i);
1537 remove_properties (properties, Qnil, i, object);
1538 if (BUFFERP (object))
1539 signal_after_change (XINT (start), XINT (end) - XINT (start),
1540 XINT (end) - XINT (start));
1541 return Qt;
1544 len -= LENGTH (i);
1545 modified |= remove_properties (properties, Qnil, i, object);
1546 i = next_interval (i);
1550 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1551 Sremove_list_of_text_properties, 3, 4, 0,
1552 doc: /* Remove some properties from text from START to END.
1553 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1554 If the optional fourth argument OBJECT is a buffer (or nil, which means
1555 the current buffer), START and END are buffer positions (integers or
1556 markers). If OBJECT is a string, START and END are 0-based indices into it.
1557 Return t if any property was actually removed, nil otherwise. */)
1558 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1560 INTERVAL i, unchanged;
1561 ptrdiff_t s, len;
1562 bool modified = 0;
1563 Lisp_Object properties;
1564 properties = list_of_properties;
1566 if (NILP (object))
1567 XSETBUFFER (object, current_buffer);
1569 i = validate_interval_range (object, &start, &end, soft);
1570 if (!i)
1571 return Qnil;
1573 s = XINT (start);
1574 len = XINT (end) - s;
1576 /* If there are no properties on the interval, return. */
1577 if (! interval_has_some_properties_list (properties, i))
1579 ptrdiff_t got = LENGTH (i) - (s - i->position);
1583 if (got >= len)
1584 return Qnil;
1585 len -= got;
1586 i = next_interval (i);
1587 got = LENGTH (i);
1589 while (! interval_has_some_properties_list (properties, i));
1591 /* Split away the beginning of this interval; what we don't
1592 want to modify. */
1593 else if (i->position != s)
1595 unchanged = i;
1596 i = split_interval_right (unchanged, s - unchanged->position);
1597 copy_properties (unchanged, i);
1600 /* We are at the beginning of an interval, with len to scan.
1601 The flag `modified' records if changes have been made.
1602 When object is a buffer, we must call modify_region before changes are
1603 made and signal_after_change when we are done.
1604 We call modify_region before calling remove_properties if modified == 0,
1605 and we call signal_after_change before returning if modified != 0. */
1606 for (;;)
1608 eassert (i != 0);
1610 if (LENGTH (i) >= len)
1612 if (! interval_has_some_properties_list (properties, i))
1614 if (modified)
1616 if (BUFFERP (object))
1617 signal_after_change (XINT (start),
1618 XINT (end) - XINT (start),
1619 XINT (end) - XINT (start));
1620 return Qt;
1622 else
1623 return Qnil;
1625 else if (LENGTH (i) == len)
1627 if (!modified && BUFFERP (object))
1628 modify_region (object, start, end);
1629 remove_properties (Qnil, properties, i, object);
1630 if (BUFFERP (object))
1631 signal_after_change (XINT (start), XINT (end) - XINT (start),
1632 XINT (end) - XINT (start));
1633 return Qt;
1635 else
1636 { /* i has the properties, and goes past the change limit. */
1637 unchanged = i;
1638 i = split_interval_left (i, len);
1639 copy_properties (unchanged, i);
1640 if (!modified && BUFFERP (object))
1641 modify_region (object, start, end);
1642 remove_properties (Qnil, properties, i, object);
1643 if (BUFFERP (object))
1644 signal_after_change (XINT (start), XINT (end) - XINT (start),
1645 XINT (end) - XINT (start));
1646 return Qt;
1649 if (interval_has_some_properties_list (properties, i))
1651 if (!modified && BUFFERP (object))
1652 modify_region (object, start, end);
1653 remove_properties (Qnil, properties, i, object);
1654 modified = 1;
1656 len -= LENGTH (i);
1657 i = next_interval (i);
1661 DEFUN ("text-property-any", Ftext_property_any,
1662 Stext_property_any, 4, 5, 0,
1663 doc: /* Check text from START to END for property PROPERTY equaling VALUE.
1664 If so, return the position of the first character whose property PROPERTY
1665 is `eq' to VALUE. Otherwise return nil.
1666 If the optional fifth argument OBJECT is a buffer (or nil, which means
1667 the current buffer), START and END are buffer positions (integers or
1668 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1669 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1671 register INTERVAL i;
1672 register ptrdiff_t e, pos;
1674 if (NILP (object))
1675 XSETBUFFER (object, current_buffer);
1676 i = validate_interval_range (object, &start, &end, soft);
1677 if (!i)
1678 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1679 e = XINT (end);
1681 while (i)
1683 if (i->position >= e)
1684 break;
1685 if (EQ (textget (i->plist, property), value))
1687 pos = i->position;
1688 if (pos < XINT (start))
1689 pos = XINT (start);
1690 return make_number (pos);
1692 i = next_interval (i);
1694 return Qnil;
1697 DEFUN ("text-property-not-all", Ftext_property_not_all,
1698 Stext_property_not_all, 4, 5, 0,
1699 doc: /* Check text from START to END for property PROPERTY not equaling VALUE.
1700 If so, return the position of the first character whose property PROPERTY
1701 is not `eq' to VALUE. Otherwise, return nil.
1702 If the optional fifth argument OBJECT is a buffer (or nil, which means
1703 the current buffer), START and END are buffer positions (integers or
1704 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1705 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1707 register INTERVAL i;
1708 register ptrdiff_t s, e;
1710 if (NILP (object))
1711 XSETBUFFER (object, current_buffer);
1712 i = validate_interval_range (object, &start, &end, soft);
1713 if (!i)
1714 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1715 s = XINT (start);
1716 e = XINT (end);
1718 while (i)
1720 if (i->position >= e)
1721 break;
1722 if (! EQ (textget (i->plist, property), value))
1724 if (i->position > s)
1725 s = i->position;
1726 return make_number (s);
1728 i = next_interval (i);
1730 return Qnil;
1734 /* Return the direction from which the text-property PROP would be
1735 inherited by any new text inserted at POS: 1 if it would be
1736 inherited from the char after POS, -1 if it would be inherited from
1737 the char before POS, and 0 if from neither.
1738 BUFFER can be either a buffer or nil (meaning current buffer). */
1741 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1743 Lisp_Object prev_pos, front_sticky;
1744 bool is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1745 Lisp_Object defalt = Fassq (prop, Vtext_property_default_nonsticky);
1747 if (NILP (buffer))
1748 XSETBUFFER (buffer, current_buffer);
1750 if (CONSP (defalt) && !NILP (XCDR (defalt)))
1751 is_rear_sticky = 0;
1753 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1754 /* Consider previous character. */
1756 Lisp_Object rear_non_sticky;
1758 prev_pos = make_number (XINT (pos) - 1);
1759 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1761 if (!NILP (CONSP (rear_non_sticky)
1762 ? Fmemq (prop, rear_non_sticky)
1763 : rear_non_sticky))
1764 /* PROP is rear-non-sticky. */
1765 is_rear_sticky = 0;
1767 else
1768 return 0;
1770 /* Consider following character. */
1771 /* This signals an arg-out-of-range error if pos is outside the
1772 buffer's accessible range. */
1773 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1775 if (EQ (front_sticky, Qt)
1776 || (CONSP (front_sticky)
1777 && !NILP (Fmemq (prop, front_sticky))))
1778 /* PROP is inherited from after. */
1779 is_front_sticky = 1;
1781 /* Simple cases, where the properties are consistent. */
1782 if (is_rear_sticky && !is_front_sticky)
1783 return -1;
1784 else if (!is_rear_sticky && is_front_sticky)
1785 return 1;
1786 else if (!is_rear_sticky && !is_front_sticky)
1787 return 0;
1789 /* The stickiness properties are inconsistent, so we have to
1790 disambiguate. Basically, rear-sticky wins, _except_ if the
1791 property that would be inherited has a value of nil, in which case
1792 front-sticky wins. */
1793 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1794 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1795 return 1;
1796 else
1797 return -1;
1801 /* Copying properties between objects. */
1803 /* Add properties from START to END of SRC, starting at POS in DEST.
1804 SRC and DEST may each refer to strings or buffers.
1805 Optional sixth argument PROP causes only that property to be copied.
1806 Properties are copied to DEST as if by `add-text-properties'.
1807 Return t if any property value actually changed, nil otherwise. */
1809 /* Note this can GC when DEST is a buffer. */
1811 Lisp_Object
1812 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src, Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1814 INTERVAL i;
1815 Lisp_Object res;
1816 Lisp_Object stuff;
1817 Lisp_Object plist;
1818 ptrdiff_t s, e, e2, p, len;
1819 bool modified = 0;
1820 struct gcpro gcpro1, gcpro2;
1822 i = validate_interval_range (src, &start, &end, soft);
1823 if (!i)
1824 return Qnil;
1826 CHECK_NUMBER_COERCE_MARKER (pos);
1828 Lisp_Object dest_start, dest_end;
1830 e = XINT (pos) + (XINT (end) - XINT (start));
1831 if (MOST_POSITIVE_FIXNUM < e)
1832 args_out_of_range (pos, end);
1833 dest_start = pos;
1834 XSETFASTINT (dest_end, e);
1835 /* Apply this to a copy of pos; it will try to increment its arguments,
1836 which we don't want. */
1837 validate_interval_range (dest, &dest_start, &dest_end, soft);
1840 s = XINT (start);
1841 e = XINT (end);
1842 p = XINT (pos);
1844 stuff = Qnil;
1846 while (s < e)
1848 e2 = i->position + LENGTH (i);
1849 if (e2 > e)
1850 e2 = e;
1851 len = e2 - s;
1853 plist = i->plist;
1854 if (! NILP (prop))
1855 while (! NILP (plist))
1857 if (EQ (Fcar (plist), prop))
1859 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1860 break;
1862 plist = Fcdr (Fcdr (plist));
1864 if (! NILP (plist))
1866 /* Must defer modifications to the interval tree in case src
1867 and dest refer to the same string or buffer. */
1868 stuff = Fcons (Fcons (make_number (p),
1869 Fcons (make_number (p + len),
1870 Fcons (plist, Qnil))),
1871 stuff);
1874 i = next_interval (i);
1875 if (!i)
1876 break;
1878 p += len;
1879 s = i->position;
1882 GCPRO2 (stuff, dest);
1884 while (! NILP (stuff))
1886 res = Fcar (stuff);
1887 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1888 Fcar (Fcdr (Fcdr (res))), dest);
1889 if (! NILP (res))
1890 modified = 1;
1891 stuff = Fcdr (stuff);
1894 UNGCPRO;
1896 return modified ? Qt : Qnil;
1900 /* Return a list representing the text properties of OBJECT between
1901 START and END. if PROP is non-nil, report only on that property.
1902 Each result list element has the form (S E PLIST), where S and E
1903 are positions in OBJECT and PLIST is a property list containing the
1904 text properties of OBJECT between S and E. Value is nil if OBJECT
1905 doesn't contain text properties between START and END. */
1907 Lisp_Object
1908 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1910 struct interval *i;
1911 Lisp_Object result;
1913 result = Qnil;
1915 i = validate_interval_range (object, &start, &end, soft);
1916 if (i)
1918 ptrdiff_t s = XINT (start);
1919 ptrdiff_t e = XINT (end);
1921 while (s < e)
1923 ptrdiff_t interval_end, len;
1924 Lisp_Object plist;
1926 interval_end = i->position + LENGTH (i);
1927 if (interval_end > e)
1928 interval_end = e;
1929 len = interval_end - s;
1931 plist = i->plist;
1933 if (!NILP (prop))
1934 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1935 if (EQ (XCAR (plist), prop))
1937 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1938 break;
1941 if (!NILP (plist))
1942 result = Fcons (Fcons (make_number (s),
1943 Fcons (make_number (s + len),
1944 Fcons (plist, Qnil))),
1945 result);
1947 i = next_interval (i);
1948 if (!i)
1949 break;
1950 s = i->position;
1954 return result;
1958 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1959 (START END PLIST), where START and END are positions and PLIST is a
1960 property list containing the text properties to add. Adjust START
1961 and END positions by DELTA before adding properties. */
1963 void
1964 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
1966 struct gcpro gcpro1, gcpro2;
1968 GCPRO2 (list, object);
1970 for (; CONSP (list); list = XCDR (list))
1972 Lisp_Object item, start, end, plist;
1974 item = XCAR (list);
1975 start = make_number (XINT (XCAR (item)) + XINT (delta));
1976 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1977 plist = XCAR (XCDR (XCDR (item)));
1979 Fadd_text_properties (start, end, plist, object);
1982 UNGCPRO;
1987 /* Modify end-points of ranges in LIST destructively, and return the
1988 new list. LIST is a list as returned from text_property_list.
1989 Discard properties that begin at or after NEW_END, and limit
1990 end-points to NEW_END. */
1992 Lisp_Object
1993 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
1995 Lisp_Object prev = Qnil, head = list;
1996 ptrdiff_t max = XINT (new_end);
1998 for (; CONSP (list); prev = list, list = XCDR (list))
2000 Lisp_Object item, beg, end;
2002 item = XCAR (list);
2003 beg = XCAR (item);
2004 end = XCAR (XCDR (item));
2006 if (XINT (beg) >= max)
2008 /* The start-point is past the end of the new string.
2009 Discard this property. */
2010 if (EQ (head, list))
2011 head = XCDR (list);
2012 else
2013 XSETCDR (prev, XCDR (list));
2015 else if (XINT (end) > max)
2016 /* The end-point is past the end of the new string. */
2017 XSETCAR (XCDR (item), new_end);
2020 return head;
2025 /* Call the modification hook functions in LIST, each with START and END. */
2027 static void
2028 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2030 struct gcpro gcpro1;
2031 GCPRO1 (list);
2032 while (!NILP (list))
2034 call2 (Fcar (list), start, end);
2035 list = Fcdr (list);
2037 UNGCPRO;
2040 /* Check for read-only intervals between character positions START ... END,
2041 in BUF, and signal an error if we find one.
2043 Then check for any modification hooks in the range.
2044 Create a list of all these hooks in lexicographic order,
2045 eliminating consecutive extra copies of the same hook. Then call
2046 those hooks in order, with START and END - 1 as arguments. */
2048 void
2049 verify_interval_modification (struct buffer *buf,
2050 ptrdiff_t start, ptrdiff_t end)
2052 INTERVAL intervals = buffer_intervals (buf);
2053 INTERVAL i;
2054 Lisp_Object hooks;
2055 Lisp_Object prev_mod_hooks;
2056 Lisp_Object mod_hooks;
2057 struct gcpro gcpro1;
2059 hooks = Qnil;
2060 prev_mod_hooks = Qnil;
2061 mod_hooks = Qnil;
2063 interval_insert_behind_hooks = Qnil;
2064 interval_insert_in_front_hooks = Qnil;
2066 if (!intervals)
2067 return;
2069 if (start > end)
2071 ptrdiff_t temp = start;
2072 start = end;
2073 end = temp;
2076 /* For an insert operation, check the two chars around the position. */
2077 if (start == end)
2079 INTERVAL prev = NULL;
2080 Lisp_Object before, after;
2082 /* Set I to the interval containing the char after START,
2083 and PREV to the interval containing the char before START.
2084 Either one may be null. They may be equal. */
2085 i = find_interval (intervals, start);
2087 if (start == BUF_BEGV (buf))
2088 prev = 0;
2089 else if (i->position == start)
2090 prev = previous_interval (i);
2091 else if (i->position < start)
2092 prev = i;
2093 if (start == BUF_ZV (buf))
2094 i = 0;
2096 /* If Vinhibit_read_only is set and is not a list, we can
2097 skip the read_only checks. */
2098 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2100 /* If I and PREV differ we need to check for the read-only
2101 property together with its stickiness. If either I or
2102 PREV are 0, this check is all we need.
2103 We have to take special care, since read-only may be
2104 indirectly defined via the category property. */
2105 if (i != prev)
2107 if (i)
2109 after = textget (i->plist, Qread_only);
2111 /* If interval I is read-only and read-only is
2112 front-sticky, inhibit insertion.
2113 Check for read-only as well as category. */
2114 if (! NILP (after)
2115 && NILP (Fmemq (after, Vinhibit_read_only)))
2117 Lisp_Object tem;
2119 tem = textget (i->plist, Qfront_sticky);
2120 if (TMEM (Qread_only, tem)
2121 || (NILP (Fplist_get (i->plist, Qread_only))
2122 && TMEM (Qcategory, tem)))
2123 text_read_only (after);
2127 if (prev)
2129 before = textget (prev->plist, Qread_only);
2131 /* If interval PREV is read-only and read-only isn't
2132 rear-nonsticky, inhibit insertion.
2133 Check for read-only as well as category. */
2134 if (! NILP (before)
2135 && NILP (Fmemq (before, Vinhibit_read_only)))
2137 Lisp_Object tem;
2139 tem = textget (prev->plist, Qrear_nonsticky);
2140 if (! TMEM (Qread_only, tem)
2141 && (! NILP (Fplist_get (prev->plist,Qread_only))
2142 || ! TMEM (Qcategory, tem)))
2143 text_read_only (before);
2147 else if (i)
2149 after = textget (i->plist, Qread_only);
2151 /* If interval I is read-only and read-only is
2152 front-sticky, inhibit insertion.
2153 Check for read-only as well as category. */
2154 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2156 Lisp_Object tem;
2158 tem = textget (i->plist, Qfront_sticky);
2159 if (TMEM (Qread_only, tem)
2160 || (NILP (Fplist_get (i->plist, Qread_only))
2161 && TMEM (Qcategory, tem)))
2162 text_read_only (after);
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 (after);
2173 /* Run both insert hooks (just once if they're the same). */
2174 if (prev)
2175 interval_insert_behind_hooks
2176 = textget (prev->plist, Qinsert_behind_hooks);
2177 if (i)
2178 interval_insert_in_front_hooks
2179 = textget (i->plist, Qinsert_in_front_hooks);
2181 else
2183 /* Loop over intervals on or next to START...END,
2184 collecting their hooks. */
2186 i = find_interval (intervals, start);
2189 if (! INTERVAL_WRITABLE_P (i))
2190 text_read_only (textget (i->plist, Qread_only));
2192 if (!inhibit_modification_hooks)
2194 mod_hooks = textget (i->plist, Qmodification_hooks);
2195 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2197 hooks = Fcons (mod_hooks, hooks);
2198 prev_mod_hooks = mod_hooks;
2202 i = next_interval (i);
2204 /* Keep going thru the interval containing the char before END. */
2205 while (i && i->position < end);
2207 if (!inhibit_modification_hooks)
2209 GCPRO1 (hooks);
2210 hooks = Fnreverse (hooks);
2211 while (! EQ (hooks, Qnil))
2213 call_mod_hooks (Fcar (hooks), make_number (start),
2214 make_number (end));
2215 hooks = Fcdr (hooks);
2217 UNGCPRO;
2222 /* Run the interval hooks for an insertion on character range START ... END.
2223 verify_interval_modification chose which hooks to run;
2224 this function is called after the insertion happens
2225 so it can indicate the range of inserted text. */
2227 void
2228 report_interval_modification (Lisp_Object start, Lisp_Object end)
2230 if (! NILP (interval_insert_behind_hooks))
2231 call_mod_hooks (interval_insert_behind_hooks, start, end);
2232 if (! NILP (interval_insert_in_front_hooks)
2233 && ! EQ (interval_insert_in_front_hooks,
2234 interval_insert_behind_hooks))
2235 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2238 void
2239 syms_of_textprop (void)
2241 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties,
2242 doc: /* Property-list used as default values.
2243 The value of a property in this list is seen as the value for every
2244 character that does not have its own value for that property. */);
2245 Vdefault_text_properties = Qnil;
2247 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist,
2248 doc: /* Alist of alternative properties for properties without a value.
2249 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2250 If a piece of text has no direct value for a particular property, then
2251 this alist is consulted. If that property appears in the alist, then
2252 the first non-nil value from the associated alternative properties is
2253 returned. */);
2254 Vchar_property_alias_alist = Qnil;
2256 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks,
2257 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2258 This also inhibits the use of the `intangible' text property. */);
2259 Vinhibit_point_motion_hooks = Qnil;
2261 DEFVAR_LISP ("text-property-default-nonsticky",
2262 Vtext_property_default_nonsticky,
2263 doc: /* Alist of properties vs the corresponding non-stickiness.
2264 Each element has the form (PROPERTY . NONSTICKINESS).
2266 If a character in a buffer has PROPERTY, new text inserted adjacent to
2267 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2268 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2269 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2270 /* Text properties `syntax-table'and `display' should be nonsticky
2271 by default. */
2272 Vtext_property_default_nonsticky
2273 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt),
2274 Fcons (Fcons (intern_c_string ("display"), Qt), Qnil));
2276 staticpro (&interval_insert_behind_hooks);
2277 staticpro (&interval_insert_in_front_hooks);
2278 interval_insert_behind_hooks = Qnil;
2279 interval_insert_in_front_hooks = Qnil;
2282 /* Common attributes one might give text */
2284 DEFSYM (Qforeground, "foreground");
2285 DEFSYM (Qbackground, "background");
2286 DEFSYM (Qfont, "font");
2287 DEFSYM (Qstipple, "stipple");
2288 DEFSYM (Qunderline, "underline");
2289 DEFSYM (Qread_only, "read-only");
2290 DEFSYM (Qinvisible, "invisible");
2291 DEFSYM (Qintangible, "intangible");
2292 DEFSYM (Qcategory, "category");
2293 DEFSYM (Qlocal_map, "local-map");
2294 DEFSYM (Qfront_sticky, "front-sticky");
2295 DEFSYM (Qrear_nonsticky, "rear-nonsticky");
2296 DEFSYM (Qmouse_face, "mouse-face");
2297 DEFSYM (Qminibuffer_prompt, "minibuffer-prompt");
2299 /* Properties that text might use to specify certain actions */
2301 DEFSYM (Qmouse_left, "mouse-left");
2302 DEFSYM (Qmouse_entered, "mouse-entered");
2303 DEFSYM (Qpoint_left, "point-left");
2304 DEFSYM (Qpoint_entered, "point-entered");
2306 defsubr (&Stext_properties_at);
2307 defsubr (&Sget_text_property);
2308 defsubr (&Sget_char_property);
2309 defsubr (&Sget_char_property_and_overlay);
2310 defsubr (&Snext_char_property_change);
2311 defsubr (&Sprevious_char_property_change);
2312 defsubr (&Snext_single_char_property_change);
2313 defsubr (&Sprevious_single_char_property_change);
2314 defsubr (&Snext_property_change);
2315 defsubr (&Snext_single_property_change);
2316 defsubr (&Sprevious_property_change);
2317 defsubr (&Sprevious_single_property_change);
2318 defsubr (&Sadd_text_properties);
2319 defsubr (&Sput_text_property);
2320 defsubr (&Sset_text_properties);
2321 defsubr (&Sremove_text_properties);
2322 defsubr (&Sremove_list_of_text_properties);
2323 defsubr (&Stext_property_any);
2324 defsubr (&Stext_property_not_all);