Fix minor problems with loaddefs autogeneration
[emacs.git] / src / textprop.c
blob225ff28e57ee44f2a84aaece4b68a7acef3323e3
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2017 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 (at
10 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 "buffer.h"
25 #include "window.h"
27 /* Test for membership, allowing for t (actually any non-cons) to mean the
28 universal set. */
30 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
33 /* NOTES: previous- and next- property change will have to skip
34 zero-length intervals if they are implemented. This could be done
35 inside next_interval and previous_interval.
37 set_properties needs to deal with the interval property cache.
39 It is assumed that for any interval plist, a property appears
40 only once on the list. Although some code i.e., remove_properties,
41 handles the more general case, the uniqueness of properties is
42 necessary for the system to remain consistent. This requirement
43 is enforced by the subrs installing properties onto the intervals. */
47 enum property_set_type
49 TEXT_PROPERTY_REPLACE,
50 TEXT_PROPERTY_PREPEND,
51 TEXT_PROPERTY_APPEND
54 /* If o1 is a cons whose cdr is a cons, return true and set o2 to
55 the o1's cdr. Otherwise, return false. This is handy for
56 traversing plists. */
57 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
59 /* verify_interval_modification saves insertion hooks here
60 to be run later by report_interval_modification. */
61 static Lisp_Object interval_insert_behind_hooks;
62 static Lisp_Object interval_insert_in_front_hooks;
65 /* Signal a `text-read-only' error. This function makes it easier
66 to capture that error in GDB by putting a breakpoint on it. */
68 static _Noreturn void
69 text_read_only (Lisp_Object propval)
71 if (STRINGP (propval))
72 xsignal1 (Qtext_read_only, propval);
74 xsignal0 (Qtext_read_only);
77 /* Prepare to modify the text properties of BUFFER from START to END. */
79 static void
80 modify_text_properties (Lisp_Object buffer, Lisp_Object start, Lisp_Object end)
82 ptrdiff_t b = XINT (start), e = XINT (end);
83 struct buffer *buf = XBUFFER (buffer), *old = current_buffer;
85 set_buffer_internal (buf);
87 prepare_to_modify_buffer_1 (b, e, NULL);
89 BUF_COMPUTE_UNCHANGED (buf, b - 1, e);
90 if (MODIFF <= SAVE_MODIFF)
91 record_first_change ();
92 MODIFF++;
94 bset_point_before_scroll (current_buffer, Qnil);
96 set_buffer_internal (old);
99 /* Complain if object is not string or buffer type. */
101 static void
102 CHECK_STRING_OR_BUFFER (Lisp_Object x)
104 CHECK_TYPE (STRINGP (x) || BUFFERP (x), Qbuffer_or_string_p, x);
107 /* Extract the interval at the position pointed to by BEGIN from
108 OBJECT, a string or buffer. Additionally, check that the positions
109 pointed to by BEGIN and END are within the bounds of OBJECT, and
110 reverse them if *BEGIN is greater than *END. The objects pointed
111 to by BEGIN and END may be integers or markers; if the latter, they
112 are coerced to integers.
114 When OBJECT is a string, we increment *BEGIN and *END
115 to make them origin-one.
117 Note that buffer points don't correspond to interval indices.
118 For example, point-max is 1 greater than the index of the last
119 character. This difference is handled in the caller, which uses
120 the validated points to determine a length, and operates on that.
121 Exceptions are Ftext_properties_at, Fnext_property_change, and
122 Fprevious_property_change which call this function with BEGIN == END.
123 Handle this case specially.
125 If FORCE is soft (false), it's OK to return NULL. Otherwise,
126 create an interval tree for OBJECT if one doesn't exist, provided
127 the object actually contains text. In the current design, if there
128 is no text, there can be no text properties. */
130 enum { soft = false, hard = true };
132 INTERVAL
133 validate_interval_range (Lisp_Object object, Lisp_Object *begin,
134 Lisp_Object *end, bool force)
136 INTERVAL i;
137 ptrdiff_t searchpos;
139 CHECK_STRING_OR_BUFFER (object);
140 CHECK_NUMBER_COERCE_MARKER (*begin);
141 CHECK_NUMBER_COERCE_MARKER (*end);
143 /* If we are asked for a point, but from a subr which operates
144 on a range, then return nothing. */
145 if (EQ (*begin, *end) && begin != end)
146 return NULL;
148 if (XINT (*begin) > XINT (*end))
150 Lisp_Object n;
151 n = *begin;
152 *begin = *end;
153 *end = n;
156 if (BUFFERP (object))
158 register struct buffer *b = XBUFFER (object);
160 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
161 && XINT (*end) <= BUF_ZV (b)))
162 args_out_of_range (*begin, *end);
163 i = buffer_intervals (b);
165 /* If there's no text, there are no properties. */
166 if (BUF_BEGV (b) == BUF_ZV (b))
167 return NULL;
169 searchpos = XINT (*begin);
171 else
173 ptrdiff_t len = SCHARS (object);
175 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
176 && XINT (*end) <= len))
177 args_out_of_range (*begin, *end);
178 XSETFASTINT (*begin, XFASTINT (*begin));
179 if (begin != end)
180 XSETFASTINT (*end, XFASTINT (*end));
181 i = string_intervals (object);
183 if (len == 0)
184 return NULL;
186 searchpos = XINT (*begin);
189 if (!i)
190 return (force ? create_root_interval (object) : i);
192 return find_interval (i, searchpos);
195 /* Validate LIST as a property list. If LIST is not a list, then
196 make one consisting of (LIST nil). Otherwise, verify that LIST
197 is even numbered and thus suitable as a plist. */
199 static Lisp_Object
200 validate_plist (Lisp_Object list)
202 if (NILP (list))
203 return Qnil;
205 if (CONSP (list))
207 Lisp_Object tail = list;
210 tail = XCDR (tail);
211 if (! CONSP (tail))
212 error ("Odd length text property list");
213 tail = XCDR (tail);
214 maybe_quit ();
216 while (CONSP (tail));
218 return list;
221 return list2 (list, Qnil);
224 /* Return true if interval I has all the properties,
225 with the same values, of list PLIST. */
227 static bool
228 interval_has_all_properties (Lisp_Object plist, INTERVAL i)
230 Lisp_Object tail1, tail2;
232 /* Go through each element of PLIST. */
233 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
235 Lisp_Object sym1 = XCAR (tail1);
236 bool found = false;
238 /* Go through I's plist, looking for sym1 */
239 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
240 if (EQ (sym1, XCAR (tail2)))
242 /* Found the same property on both lists. If the
243 values are unequal, return false. */
244 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
245 return false;
247 /* Property has same value on both lists; go to next one. */
248 found = true;
249 break;
252 if (! found)
253 return false;
256 return true;
259 /* Return true if the plist of interval I has any of the
260 properties of PLIST, regardless of their values. */
262 static bool
263 interval_has_some_properties (Lisp_Object plist, INTERVAL i)
265 Lisp_Object tail1, tail2, sym;
267 /* Go through each element of PLIST. */
268 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
270 sym = XCAR (tail1);
272 /* Go through i's plist, looking for tail1 */
273 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
274 if (EQ (sym, XCAR (tail2)))
275 return true;
278 return false;
281 /* Return true if the plist of interval I has any of the
282 property names in LIST, regardless of their values. */
284 static bool
285 interval_has_some_properties_list (Lisp_Object list, INTERVAL i)
287 Lisp_Object tail1, tail2, sym;
289 /* Go through each element of LIST. */
290 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
292 sym = XCAR (tail1);
294 /* Go through i's plist, looking for tail1 */
295 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
296 if (EQ (sym, XCAR (tail2)))
297 return true;
300 return false;
303 /* Changing the plists of individual intervals. */
305 /* Return the value of PROP in property-list PLIST, or Qunbound if it
306 has none. */
307 static Lisp_Object
308 property_value (Lisp_Object plist, Lisp_Object prop)
310 Lisp_Object value;
312 while (PLIST_ELT_P (plist, value))
313 if (EQ (XCAR (plist), prop))
314 return XCAR (value);
315 else
316 plist = XCDR (value);
318 return Qunbound;
321 /* Set the properties of INTERVAL to PROPERTIES,
322 and record undo info for the previous values.
323 OBJECT is the string or buffer that INTERVAL belongs to. */
325 static void
326 set_properties (Lisp_Object properties, INTERVAL interval, Lisp_Object object)
328 Lisp_Object sym, value;
330 if (BUFFERP (object))
332 /* For each property in the old plist which is missing from PROPERTIES,
333 or has a different value in PROPERTIES, make an undo record. */
334 for (sym = interval->plist;
335 PLIST_ELT_P (sym, value);
336 sym = XCDR (value))
337 if (! EQ (property_value (properties, XCAR (sym)),
338 XCAR (value)))
340 record_property_change (interval->position, LENGTH (interval),
341 XCAR (sym), XCAR (value),
342 object);
345 /* For each new property that has no value at all in the old plist,
346 make an undo record binding it to nil, so it will be removed. */
347 for (sym = properties;
348 PLIST_ELT_P (sym, value);
349 sym = XCDR (value))
350 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
352 record_property_change (interval->position, LENGTH (interval),
353 XCAR (sym), Qnil,
354 object);
358 /* Store new properties. */
359 set_interval_plist (interval, Fcopy_sequence (properties));
362 /* Add the properties of PLIST to the interval I, or set
363 the value of I's property to the value of the property on PLIST
364 if they are different.
366 OBJECT should be the string or buffer the interval is in.
368 Return true if this changes I (i.e., if any members of PLIST
369 are actually added to I's plist) */
371 static bool
372 add_properties (Lisp_Object plist, INTERVAL i, Lisp_Object object,
373 enum property_set_type set_type)
375 Lisp_Object tail1, tail2, sym1, val1;
376 bool changed = false;
378 tail1 = plist;
379 sym1 = Qnil;
380 val1 = Qnil;
382 /* Go through each element of PLIST. */
383 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
385 bool found = false;
386 sym1 = XCAR (tail1);
387 val1 = Fcar (XCDR (tail1));
389 /* Go through I's plist, looking for sym1 */
390 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
391 if (EQ (sym1, XCAR (tail2)))
393 Lisp_Object this_cdr;
395 this_cdr = XCDR (tail2);
396 /* Found the property. Now check its value. */
397 found = true;
399 /* The properties have the same value on both lists.
400 Continue to the next property. */
401 if (EQ (val1, Fcar (this_cdr)))
402 break;
404 /* Record this change in the buffer, for undo purposes. */
405 if (BUFFERP (object))
407 record_property_change (i->position, LENGTH (i),
408 sym1, Fcar (this_cdr), object);
411 /* I's property has a different value -- change it */
412 if (set_type == TEXT_PROPERTY_REPLACE)
413 Fsetcar (this_cdr, val1);
414 else {
415 if (CONSP (Fcar (this_cdr)) &&
416 /* Special-case anonymous face properties. */
417 (! EQ (sym1, Qface) ||
418 NILP (Fkeywordp (Fcar (Fcar (this_cdr))))))
419 /* The previous value is a list, so prepend (or
420 append) the new value to this list. */
421 if (set_type == TEXT_PROPERTY_PREPEND)
422 Fsetcar (this_cdr, Fcons (val1, Fcar (this_cdr)));
423 else
424 nconc2 (Fcar (this_cdr), list1 (val1));
425 else {
426 /* The previous value is a single value, so make it
427 into a list. */
428 if (set_type == TEXT_PROPERTY_PREPEND)
429 Fsetcar (this_cdr, list2 (val1, Fcar (this_cdr)));
430 else
431 Fsetcar (this_cdr, list2 (Fcar (this_cdr), val1));
434 changed = true;
435 break;
438 if (! found)
440 /* Record this change in the buffer, for undo purposes. */
441 if (BUFFERP (object))
443 record_property_change (i->position, LENGTH (i),
444 sym1, Qnil, object);
446 set_interval_plist (i, Fcons (sym1, Fcons (val1, i->plist)));
447 changed = true;
451 return changed;
454 /* For any members of PLIST, or LIST,
455 which are properties of I, remove them from I's plist.
456 (If PLIST is non-nil, use that, otherwise use LIST.)
457 OBJECT is the string or buffer containing I. */
459 static bool
460 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
462 bool changed = false;
464 /* True means tail1 is a plist, otherwise it is a list. */
465 bool use_plist = ! NILP (plist);
466 Lisp_Object tail1 = use_plist ? plist : list;
468 Lisp_Object current_plist = i->plist;
470 /* Go through each element of LIST or PLIST. */
471 while (CONSP (tail1))
473 Lisp_Object sym = XCAR (tail1);
475 /* First, remove the symbol if it's at the head of the list */
476 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
478 if (BUFFERP (object))
479 record_property_change (i->position, LENGTH (i),
480 sym, XCAR (XCDR (current_plist)),
481 object);
483 current_plist = XCDR (XCDR (current_plist));
484 changed = true;
487 /* Go through I's plist, looking for SYM. */
488 Lisp_Object tail2 = current_plist;
489 while (! NILP (tail2))
491 Lisp_Object this = XCDR (XCDR (tail2));
492 if (CONSP (this) && EQ (sym, XCAR (this)))
494 if (BUFFERP (object))
495 record_property_change (i->position, LENGTH (i),
496 sym, XCAR (XCDR (this)), object);
498 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
499 changed = true;
501 tail2 = this;
504 /* Advance thru TAIL1 one way or the other. */
505 tail1 = XCDR (tail1);
506 if (use_plist && CONSP (tail1))
507 tail1 = XCDR (tail1);
510 if (changed)
511 set_interval_plist (i, current_plist);
512 return changed;
515 /* Returns the interval of POSITION in OBJECT.
516 POSITION is BEG-based. */
518 INTERVAL
519 interval_of (ptrdiff_t position, Lisp_Object object)
521 register INTERVAL i;
522 ptrdiff_t beg, end;
524 if (NILP (object))
525 XSETBUFFER (object, current_buffer);
526 else if (EQ (object, Qt))
527 return NULL;
529 CHECK_STRING_OR_BUFFER (object);
531 if (BUFFERP (object))
533 register struct buffer *b = XBUFFER (object);
535 beg = BUF_BEGV (b);
536 end = BUF_ZV (b);
537 i = buffer_intervals (b);
539 else
541 beg = 0;
542 end = SCHARS (object);
543 i = string_intervals (object);
546 if (!(beg <= position && position <= end))
547 args_out_of_range (make_number (position), make_number (position));
548 if (beg == end || !i)
549 return NULL;
551 return find_interval (i, position);
554 DEFUN ("text-properties-at", Ftext_properties_at,
555 Stext_properties_at, 1, 2, 0,
556 doc: /* Return the list of properties of the character at POSITION in OBJECT.
557 If the optional second argument OBJECT is a buffer (or nil, which means
558 the current buffer), POSITION is a buffer position (integer or marker).
559 If OBJECT is a string, POSITION is a 0-based index into it.
560 If POSITION is at the end of OBJECT, the value is nil. */)
561 (Lisp_Object position, Lisp_Object object)
563 register INTERVAL i;
565 if (NILP (object))
566 XSETBUFFER (object, current_buffer);
568 i = validate_interval_range (object, &position, &position, soft);
569 if (!i)
570 return Qnil;
571 /* If POSITION is at the end of the interval,
572 it means it's the end of OBJECT.
573 There are no properties at the very end,
574 since no character follows. */
575 if (XINT (position) == LENGTH (i) + i->position)
576 return Qnil;
578 return i->plist;
581 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
582 doc: /* Return the value of POSITION's property PROP, in OBJECT.
583 OBJECT should be a buffer or a string; if omitted or nil, it defaults
584 to the current buffer.
585 If POSITION is at the end of OBJECT, the value is nil. */)
586 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
588 return textget (Ftext_properties_at (position, object), prop);
591 /* Return the value of char's property PROP, in OBJECT at POSITION.
592 OBJECT is optional and defaults to the current buffer.
593 If OVERLAY is non-0, then in the case that the returned property is from
594 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
595 returned in *OVERLAY.
596 If POSITION is at the end of OBJECT, the value is nil.
597 If OBJECT is a buffer, then overlay properties are considered as well as
598 text properties.
599 If OBJECT is a window, then that window's buffer is used, but
600 window-specific overlays are considered only if they are associated
601 with OBJECT. */
602 Lisp_Object
603 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
605 struct window *w = 0;
607 CHECK_NUMBER_COERCE_MARKER (position);
609 if (NILP (object))
610 XSETBUFFER (object, current_buffer);
612 if (WINDOWP (object))
614 CHECK_LIVE_WINDOW (object);
615 w = XWINDOW (object);
616 object = w->contents;
618 if (BUFFERP (object))
620 ptrdiff_t noverlays;
621 Lisp_Object *overlay_vec;
622 struct buffer *obuf = current_buffer;
624 if (XINT (position) < BUF_BEGV (XBUFFER (object))
625 || XINT (position) > BUF_ZV (XBUFFER (object)))
626 xsignal1 (Qargs_out_of_range, position);
628 set_buffer_temp (XBUFFER (object));
630 USE_SAFE_ALLOCA;
631 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, false);
632 noverlays = sort_overlays (overlay_vec, noverlays, w);
634 set_buffer_temp (obuf);
636 /* Now check the overlays in order of decreasing priority. */
637 while (--noverlays >= 0)
639 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
640 if (!NILP (tem))
642 if (overlay)
643 /* Return the overlay we got the property from. */
644 *overlay = overlay_vec[noverlays];
645 SAFE_FREE ();
646 return tem;
649 SAFE_FREE ();
652 if (overlay)
653 /* Indicate that the return value is not from an overlay. */
654 *overlay = Qnil;
656 /* Not a buffer, or no appropriate overlay, so fall through to the
657 simpler case. */
658 return Fget_text_property (position, prop, object);
661 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
662 doc: /* Return the value of POSITION's property PROP, in OBJECT.
663 Both overlay properties and text properties are checked.
664 OBJECT is optional and defaults to the current buffer.
665 If POSITION is at the end of OBJECT, the value is nil.
666 If OBJECT is a buffer, then overlay properties are considered as well as
667 text properties.
668 If OBJECT is a window, then that window's buffer is used, but window-specific
669 overlays are considered only if they are associated with OBJECT. */)
670 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
672 return get_char_property_and_overlay (position, prop, object, 0);
675 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
676 Sget_char_property_and_overlay, 2, 3, 0,
677 doc: /* Like `get-char-property', but with extra overlay information.
678 The value is a cons cell. Its car is the return value of `get-char-property'
679 with the same arguments--that is, the value of POSITION's property
680 PROP in OBJECT. Its cdr is the overlay in which the property was
681 found, or nil, if it was found as a text property or not found at all.
683 OBJECT is optional and defaults to the current buffer. OBJECT may be
684 a string, a buffer or a window. For strings, the cdr of the return
685 value is always nil, since strings do not have overlays. If OBJECT is
686 a window, then that window's buffer is used, but window-specific
687 overlays are considered only if they are associated with OBJECT. If
688 POSITION is at the end of OBJECT, both car and cdr are nil. */)
689 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
691 Lisp_Object overlay;
692 Lisp_Object val
693 = get_char_property_and_overlay (position, prop, object, &overlay);
694 return Fcons (val, overlay);
698 DEFUN ("next-char-property-change", Fnext_char_property_change,
699 Snext_char_property_change, 1, 2, 0,
700 doc: /* Return the position of next text property or overlay change.
701 This scans characters forward in the current buffer from POSITION till
702 it finds a change in some text property, or the beginning or end of an
703 overlay, and returns the position of that.
704 If none is found, and LIMIT is nil or omitted, the function
705 returns (point-max).
707 If the optional second argument LIMIT is non-nil, the function doesn't
708 search past position LIMIT, and returns LIMIT if nothing is found
709 before LIMIT. LIMIT is a no-op if it is greater than (point-max). */)
710 (Lisp_Object position, Lisp_Object limit)
712 Lisp_Object temp;
714 temp = Fnext_overlay_change (position);
715 if (! NILP (limit))
717 CHECK_NUMBER_COERCE_MARKER (limit);
718 if (XINT (limit) < XINT (temp))
719 temp = limit;
721 return Fnext_property_change (position, Qnil, temp);
724 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
725 Sprevious_char_property_change, 1, 2, 0,
726 doc: /* Return the position of previous text property or overlay change.
727 Scans characters backward in the current buffer from POSITION till it
728 finds a change in some text property, or the beginning or end of an
729 overlay, and returns the position of that.
730 If none is found, and LIMIT is nil or omitted, the function
731 returns (point-min).
733 If the optional second argument LIMIT is non-nil, the function doesn't
734 search before position LIMIT, and returns LIMIT if nothing is found
735 before LIMIT. LIMIT is a no-op if it is less than (point-min). */)
736 (Lisp_Object position, Lisp_Object limit)
738 Lisp_Object temp;
740 temp = Fprevious_overlay_change (position);
741 if (! NILP (limit))
743 CHECK_NUMBER_COERCE_MARKER (limit);
744 if (XINT (limit) > XINT (temp))
745 temp = limit;
747 return Fprevious_property_change (position, Qnil, temp);
751 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
752 Snext_single_char_property_change, 2, 4, 0,
753 doc: /* Return the position of next text property or overlay change for a specific property.
754 Scans characters forward from POSITION till it finds
755 a change in the PROP property, then returns the position of the change.
756 If the optional third argument OBJECT is a buffer (or nil, which means
757 the current buffer), POSITION is a buffer position (integer or marker).
758 If OBJECT is a string, POSITION is a 0-based index into it.
760 In a string, scan runs to the end of the string, unless LIMIT is non-nil.
761 In a buffer, if LIMIT is nil or omitted, it runs to (point-max), and the
762 value cannot exceed that.
763 If the optional fourth argument LIMIT is non-nil, don't search
764 past position LIMIT; return LIMIT if nothing is found before LIMIT.
766 The property values are compared with `eq'.
767 If the property is constant all the way to the end of OBJECT, return the
768 last valid position in OBJECT. */)
769 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
771 if (STRINGP (object))
773 position = Fnext_single_property_change (position, prop, object, limit);
774 if (NILP (position))
776 if (NILP (limit))
777 position = make_number (SCHARS (object));
778 else
780 CHECK_NUMBER (limit);
781 position = limit;
785 else
787 Lisp_Object initial_value, value;
788 ptrdiff_t count = SPECPDL_INDEX ();
790 if (! NILP (object))
791 CHECK_BUFFER (object);
793 if (BUFFERP (object) && current_buffer != XBUFFER (object))
795 record_unwind_current_buffer ();
796 Fset_buffer (object);
799 CHECK_NUMBER_COERCE_MARKER (position);
801 initial_value = Fget_char_property (position, prop, object);
803 if (NILP (limit))
804 XSETFASTINT (limit, ZV);
805 else
806 CHECK_NUMBER_COERCE_MARKER (limit);
808 if (XFASTINT (position) >= XFASTINT (limit))
810 position = limit;
811 if (XFASTINT (position) > ZV)
812 XSETFASTINT (position, ZV);
814 else
815 while (true)
817 position = Fnext_char_property_change (position, limit);
818 if (XFASTINT (position) >= XFASTINT (limit))
820 position = limit;
821 break;
824 value = Fget_char_property (position, prop, object);
825 if (!EQ (value, initial_value))
826 break;
829 unbind_to (count, Qnil);
832 return position;
835 DEFUN ("previous-single-char-property-change",
836 Fprevious_single_char_property_change,
837 Sprevious_single_char_property_change, 2, 4, 0,
838 doc: /* Return the position of previous text property or overlay change for a specific property.
839 Scans characters backward from POSITION till it finds
840 a change in the PROP property, then returns the position of the change.
841 If the optional third argument OBJECT is a buffer (or nil, which means
842 the current buffer), POSITION is a buffer position (integer or marker).
843 If OBJECT is a string, POSITION is a 0-based index into it.
845 In a string, scan runs to the start of the string, unless LIMIT is non-nil.
846 In a buffer, if LIMIT is nil or omitted, it runs to (point-min), and the
847 value cannot be less than that.
848 If the optional fourth argument LIMIT is non-nil, don't search back past
849 position LIMIT; return LIMIT if nothing is found before reaching LIMIT.
851 The property values are compared with `eq'.
852 If the property is constant all the way to the start of OBJECT, return the
853 first valid position in OBJECT. */)
854 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
856 if (STRINGP (object))
858 position = Fprevious_single_property_change (position, prop, object, limit);
859 if (NILP (position))
861 if (NILP (limit))
862 position = make_number (0);
863 else
865 CHECK_NUMBER (limit);
866 position = limit;
870 else
872 ptrdiff_t count = SPECPDL_INDEX ();
874 if (! NILP (object))
875 CHECK_BUFFER (object);
877 if (BUFFERP (object) && current_buffer != XBUFFER (object))
879 record_unwind_current_buffer ();
880 Fset_buffer (object);
883 CHECK_NUMBER_COERCE_MARKER (position);
885 if (NILP (limit))
886 XSETFASTINT (limit, BEGV);
887 else
888 CHECK_NUMBER_COERCE_MARKER (limit);
890 if (XFASTINT (position) <= XFASTINT (limit))
892 position = limit;
893 if (XFASTINT (position) < BEGV)
894 XSETFASTINT (position, BEGV);
896 else
898 Lisp_Object initial_value
899 = Fget_char_property (make_number (XFASTINT (position) - 1),
900 prop, object);
902 while (true)
904 position = Fprevious_char_property_change (position, limit);
906 if (XFASTINT (position) <= XFASTINT (limit))
908 position = limit;
909 break;
911 else
913 Lisp_Object value
914 = Fget_char_property (make_number (XFASTINT (position) - 1),
915 prop, object);
917 if (!EQ (value, initial_value))
918 break;
923 unbind_to (count, Qnil);
926 return position;
929 DEFUN ("next-property-change", Fnext_property_change,
930 Snext_property_change, 1, 3, 0,
931 doc: /* Return the position of next property change.
932 Scans characters forward from POSITION in OBJECT till it finds
933 a change in some text property, then returns the position of the change.
934 If the optional second argument OBJECT is a buffer (or nil, which means
935 the current buffer), POSITION is a buffer position (integer or marker).
936 If OBJECT is a string, POSITION is a 0-based index into it.
937 Return nil if LIMIT is nil or omitted, and the property is constant all
938 the way to the end of OBJECT; if the value is non-nil, it is a position
939 greater than POSITION, never equal.
941 If the optional third argument LIMIT is non-nil, don't search
942 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
943 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
945 register INTERVAL i, next;
947 if (NILP (object))
948 XSETBUFFER (object, current_buffer);
950 if (!NILP (limit) && !EQ (limit, Qt))
951 CHECK_NUMBER_COERCE_MARKER (limit);
953 i = validate_interval_range (object, &position, &position, soft);
955 /* If LIMIT is t, return start of next interval--don't
956 bother checking further intervals. */
957 if (EQ (limit, Qt))
959 if (!i)
960 next = i;
961 else
962 next = next_interval (i);
964 if (!next)
965 XSETFASTINT (position, (STRINGP (object)
966 ? SCHARS (object)
967 : BUF_ZV (XBUFFER (object))));
968 else
969 XSETFASTINT (position, next->position);
970 return position;
973 if (!i)
974 return limit;
976 next = next_interval (i);
978 while (next && intervals_equal (i, next)
979 && (NILP (limit) || next->position < XFASTINT (limit)))
980 next = next_interval (next);
982 if (!next
983 || (next->position
984 >= (INTEGERP (limit)
985 ? XFASTINT (limit)
986 : (STRINGP (object)
987 ? SCHARS (object)
988 : BUF_ZV (XBUFFER (object))))))
989 return limit;
990 else
991 return make_number (next->position);
994 DEFUN ("next-single-property-change", Fnext_single_property_change,
995 Snext_single_property_change, 2, 4, 0,
996 doc: /* Return the position of next property change for a specific property.
997 Scans characters forward from POSITION till it finds
998 a change in the PROP property, then returns the position of the change.
999 If the optional third argument OBJECT is a buffer (or nil, which means
1000 the current buffer), POSITION is a buffer position (integer or marker).
1001 If OBJECT is a string, POSITION is a 0-based index into it.
1002 The property values are compared with `eq'.
1003 Return nil if LIMIT is nil or omitted, and the property is constant all
1004 the way to the end of OBJECT; if the value is non-nil, it is a position
1005 greater than POSITION, never equal.
1007 If the optional fourth argument LIMIT is non-nil, don't search
1008 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1009 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1011 register INTERVAL i, next;
1012 register Lisp_Object here_val;
1014 if (NILP (object))
1015 XSETBUFFER (object, current_buffer);
1017 if (!NILP (limit))
1018 CHECK_NUMBER_COERCE_MARKER (limit);
1020 i = validate_interval_range (object, &position, &position, soft);
1021 if (!i)
1022 return limit;
1024 here_val = textget (i->plist, prop);
1025 next = next_interval (i);
1026 while (next
1027 && EQ (here_val, textget (next->plist, prop))
1028 && (NILP (limit) || next->position < XFASTINT (limit)))
1029 next = next_interval (next);
1031 if (!next
1032 || (next->position
1033 >= (INTEGERP (limit)
1034 ? XFASTINT (limit)
1035 : (STRINGP (object)
1036 ? SCHARS (object)
1037 : BUF_ZV (XBUFFER (object))))))
1038 return limit;
1039 else
1040 return make_number (next->position);
1043 DEFUN ("previous-property-change", Fprevious_property_change,
1044 Sprevious_property_change, 1, 3, 0,
1045 doc: /* Return the position of previous property change.
1046 Scans characters backwards from POSITION in OBJECT till it finds
1047 a change in some text property, then returns the position of the change.
1048 If the optional second argument OBJECT is a buffer (or nil, which means
1049 the current buffer), POSITION is a buffer position (integer or marker).
1050 If OBJECT is a string, POSITION is a 0-based index into it.
1051 Return nil if LIMIT is nil or omitted, and the property is constant all
1052 the way to the start of OBJECT; if the value is non-nil, it is a position
1053 less than POSITION, never equal.
1055 If the optional third argument LIMIT is non-nil, don't search
1056 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1057 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1059 register INTERVAL i, previous;
1061 if (NILP (object))
1062 XSETBUFFER (object, current_buffer);
1064 if (!NILP (limit))
1065 CHECK_NUMBER_COERCE_MARKER (limit);
1067 i = validate_interval_range (object, &position, &position, soft);
1068 if (!i)
1069 return limit;
1071 /* Start with the interval containing the char before point. */
1072 if (i->position == XFASTINT (position))
1073 i = previous_interval (i);
1075 previous = previous_interval (i);
1076 while (previous && intervals_equal (previous, i)
1077 && (NILP (limit)
1078 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1079 previous = previous_interval (previous);
1081 if (!previous
1082 || (previous->position + LENGTH (previous)
1083 <= (INTEGERP (limit)
1084 ? XFASTINT (limit)
1085 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1086 return limit;
1087 else
1088 return make_number (previous->position + LENGTH (previous));
1091 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1092 Sprevious_single_property_change, 2, 4, 0,
1093 doc: /* Return the position of previous property change for a specific property.
1094 Scans characters backward from POSITION till it finds
1095 a change in the PROP property, then returns the position of the change.
1096 If the optional third argument OBJECT is a buffer (or nil, which means
1097 the current buffer), POSITION is a buffer position (integer or marker).
1098 If OBJECT is a string, POSITION is a 0-based index into it.
1099 The property values are compared with `eq'.
1100 Return nil if LIMIT is nil or omitted, and the property is constant all
1101 the way to the start of OBJECT; if the value is non-nil, it is a position
1102 less than POSITION, never equal.
1104 If the optional fourth argument LIMIT is non-nil, don't search
1105 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1106 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1108 register INTERVAL i, previous;
1109 register Lisp_Object here_val;
1111 if (NILP (object))
1112 XSETBUFFER (object, current_buffer);
1114 if (!NILP (limit))
1115 CHECK_NUMBER_COERCE_MARKER (limit);
1117 i = validate_interval_range (object, &position, &position, soft);
1119 /* Start with the interval containing the char before point. */
1120 if (i && i->position == XFASTINT (position))
1121 i = previous_interval (i);
1123 if (!i)
1124 return limit;
1126 here_val = textget (i->plist, prop);
1127 previous = previous_interval (i);
1128 while (previous
1129 && EQ (here_val, textget (previous->plist, prop))
1130 && (NILP (limit)
1131 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1132 previous = previous_interval (previous);
1134 if (!previous
1135 || (previous->position + LENGTH (previous)
1136 <= (INTEGERP (limit)
1137 ? XFASTINT (limit)
1138 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1139 return limit;
1140 else
1141 return make_number (previous->position + LENGTH (previous));
1144 /* Used by add-text-properties and add-face-text-property. */
1146 static Lisp_Object
1147 add_text_properties_1 (Lisp_Object start, Lisp_Object end,
1148 Lisp_Object properties, Lisp_Object object,
1149 enum property_set_type set_type) {
1150 INTERVAL i, unchanged;
1151 ptrdiff_t s, len;
1152 bool modified = false;
1153 bool first_time = true;
1155 properties = validate_plist (properties);
1156 if (NILP (properties))
1157 return Qnil;
1159 if (NILP (object))
1160 XSETBUFFER (object, current_buffer);
1162 retry:
1163 i = validate_interval_range (object, &start, &end, hard);
1164 if (!i)
1165 return Qnil;
1167 s = XINT (start);
1168 len = XINT (end) - s;
1170 /* If this interval already has the properties, we can skip it. */
1171 if (interval_has_all_properties (properties, i))
1173 ptrdiff_t got = LENGTH (i) - (s - i->position);
1177 if (got >= len)
1178 return Qnil;
1179 len -= got;
1180 i = next_interval (i);
1181 got = LENGTH (i);
1183 while (interval_has_all_properties (properties, i));
1185 else if (i->position != s)
1187 /* If we're not starting on an interval boundary, we have to
1188 split this interval. */
1189 unchanged = i;
1190 i = split_interval_right (unchanged, s - unchanged->position);
1191 copy_properties (unchanged, i);
1194 if (BUFFERP (object) && first_time)
1196 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1197 ptrdiff_t prev_pos = i->position;
1199 modify_text_properties (object, start, end);
1200 /* If someone called us recursively as a side effect of
1201 modify_text_properties, and changed the intervals behind our back
1202 (could happen if lock_file, called by prepare_to_modify_buffer,
1203 triggers redisplay, and that calls add-text-properties again
1204 in the same buffer), we cannot continue with I, because its
1205 data changed. So we restart the interval analysis anew. */
1206 if (TOTAL_LENGTH (i) != prev_total_length
1207 || i->position != prev_pos)
1209 first_time = false;
1210 goto retry;
1214 /* We are at the beginning of interval I, with LEN chars to scan. */
1215 for (;;)
1217 eassert (i != 0);
1219 if (LENGTH (i) >= len)
1221 if (interval_has_all_properties (properties, i))
1223 if (BUFFERP (object))
1224 signal_after_change (XINT (start), XINT (end) - XINT (start),
1225 XINT (end) - XINT (start));
1227 eassert (modified);
1228 return Qt;
1231 if (LENGTH (i) == len)
1233 add_properties (properties, i, object, set_type);
1234 if (BUFFERP (object))
1235 signal_after_change (XINT (start), XINT (end) - XINT (start),
1236 XINT (end) - XINT (start));
1237 return Qt;
1240 /* i doesn't have the properties, and goes past the change limit */
1241 unchanged = i;
1242 i = split_interval_left (unchanged, len);
1243 copy_properties (unchanged, i);
1244 add_properties (properties, i, object, set_type);
1245 if (BUFFERP (object))
1246 signal_after_change (XINT (start), XINT (end) - XINT (start),
1247 XINT (end) - XINT (start));
1248 return Qt;
1251 len -= LENGTH (i);
1252 modified |= add_properties (properties, i, object, set_type);
1253 i = next_interval (i);
1257 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1259 DEFUN ("add-text-properties", Fadd_text_properties,
1260 Sadd_text_properties, 3, 4, 0,
1261 doc: /* Add properties to the text from START to END.
1262 The third argument PROPERTIES is a property list
1263 specifying the property values to add. If the optional fourth argument
1264 OBJECT is a buffer (or nil, which means the current buffer),
1265 START and END are buffer positions (integers or markers).
1266 If OBJECT is a string, START and END are 0-based indices into it.
1267 Return t if any property value actually changed, nil otherwise. */)
1268 (Lisp_Object start, Lisp_Object end, Lisp_Object properties,
1269 Lisp_Object object)
1271 return add_text_properties_1 (start, end, properties, object,
1272 TEXT_PROPERTY_REPLACE);
1275 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1277 DEFUN ("put-text-property", Fput_text_property,
1278 Sput_text_property, 4, 5, 0,
1279 doc: /* Set one property of the text from START to END.
1280 The third and fourth arguments PROPERTY and VALUE
1281 specify the property to add.
1282 If the optional fifth argument OBJECT is a buffer (or nil, which means
1283 the current buffer), START and END are buffer positions (integers or
1284 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1285 (Lisp_Object start, Lisp_Object end, Lisp_Object property,
1286 Lisp_Object value, Lisp_Object object)
1288 AUTO_LIST2 (properties, property, value);
1289 Fadd_text_properties (start, end, properties, object);
1290 return Qnil;
1293 DEFUN ("set-text-properties", Fset_text_properties,
1294 Sset_text_properties, 3, 4, 0,
1295 doc: /* Completely replace properties of text from START to END.
1296 The third argument PROPERTIES is the new property list.
1297 If the optional fourth argument OBJECT is a buffer (or nil, which means
1298 the current buffer), START and END are buffer positions (integers or
1299 markers). If OBJECT is a string, START and END are 0-based indices into it.
1300 If PROPERTIES is nil, the effect is to remove all properties from
1301 the designated part of OBJECT. */)
1302 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1304 return set_text_properties (start, end, properties, object, Qt);
1308 DEFUN ("add-face-text-property", Fadd_face_text_property,
1309 Sadd_face_text_property, 3, 5, 0,
1310 doc: /* Add the face property to the text from START to END.
1311 FACE specifies the face to add. It should be a valid value of the
1312 `face' property (typically a face name or a plist of face attributes
1313 and values).
1315 If any text in the region already has a non-nil `face' property, those
1316 face(s) are retained. This is done by setting the `face' property to
1317 a list of faces, with FACE as the first element (by default) and the
1318 pre-existing faces as the remaining elements.
1320 If optional fourth argument APPEND is non-nil, append FACE to the end
1321 of the face list instead.
1323 If optional fifth argument OBJECT is a buffer (or nil, which means the
1324 current buffer), START and END are buffer positions (integers or
1325 markers). If OBJECT is a string, START and END are 0-based indices
1326 into it. */)
1327 (Lisp_Object start, Lisp_Object end, Lisp_Object face,
1328 Lisp_Object append, Lisp_Object object)
1330 AUTO_LIST2 (properties, Qface, face);
1331 add_text_properties_1 (start, end, properties, object,
1332 (NILP (append)
1333 ? TEXT_PROPERTY_PREPEND
1334 : TEXT_PROPERTY_APPEND));
1335 return Qnil;
1338 /* Replace properties of text from START to END with new list of
1339 properties PROPERTIES. OBJECT is the buffer or string containing
1340 the text. OBJECT nil means use the current buffer.
1341 COHERENT_CHANGE_P nil means this is being called as an internal
1342 subroutine, rather than as a change primitive with checking of
1343 read-only, invoking change hooks, etc.. Value is nil if the
1344 function _detected_ that it did not replace any properties, non-nil
1345 otherwise. */
1347 Lisp_Object
1348 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties,
1349 Lisp_Object object, Lisp_Object coherent_change_p)
1351 register INTERVAL i;
1352 Lisp_Object ostart, oend;
1354 ostart = start;
1355 oend = end;
1357 properties = validate_plist (properties);
1359 if (NILP (object))
1360 XSETBUFFER (object, current_buffer);
1362 /* If we want no properties for a whole string,
1363 get rid of its intervals. */
1364 if (NILP (properties) && STRINGP (object)
1365 && XFASTINT (start) == 0
1366 && XFASTINT (end) == SCHARS (object))
1368 if (!string_intervals (object))
1369 return Qnil;
1371 set_string_intervals (object, NULL);
1372 return Qt;
1375 i = validate_interval_range (object, &start, &end, soft);
1377 if (!i)
1379 /* If buffer has no properties, and we want none, return now. */
1380 if (NILP (properties))
1381 return Qnil;
1383 /* Restore the original START and END values
1384 because validate_interval_range increments them for strings. */
1385 start = ostart;
1386 end = oend;
1388 i = validate_interval_range (object, &start, &end, hard);
1389 /* This can return if start == end. */
1390 if (!i)
1391 return Qnil;
1394 if (BUFFERP (object) && !NILP (coherent_change_p))
1395 modify_text_properties (object, start, end);
1397 set_text_properties_1 (start, end, properties, object, i);
1399 if (BUFFERP (object) && !NILP (coherent_change_p))
1400 signal_after_change (XINT (start), XINT (end) - XINT (start),
1401 XINT (end) - XINT (start));
1402 return Qt;
1405 /* Replace properties of text from START to END with new list of
1406 properties PROPERTIES. OBJECT is the buffer or string containing
1407 the text. This does not obey any hooks.
1408 You should provide the interval that START is located in as I.
1409 START and END can be in any order. */
1411 void
1412 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, INTERVAL i)
1414 register INTERVAL prev_changed = NULL;
1415 register ptrdiff_t s, len;
1416 INTERVAL unchanged;
1418 if (XINT (start) < XINT (end))
1420 s = XINT (start);
1421 len = XINT (end) - s;
1423 else if (XINT (end) < XINT (start))
1425 s = XINT (end);
1426 len = XINT (start) - s;
1428 else
1429 return;
1431 eassert (i);
1433 if (i->position != s)
1435 unchanged = i;
1436 i = split_interval_right (unchanged, s - unchanged->position);
1438 if (LENGTH (i) > len)
1440 copy_properties (unchanged, i);
1441 i = split_interval_left (i, len);
1442 set_properties (properties, i, object);
1443 return;
1446 set_properties (properties, i, object);
1448 if (LENGTH (i) == len)
1449 return;
1451 prev_changed = i;
1452 len -= LENGTH (i);
1453 i = next_interval (i);
1456 /* We are starting at the beginning of an interval I. LEN is positive. */
1459 eassert (i != 0);
1461 if (LENGTH (i) >= len)
1463 if (LENGTH (i) > len)
1464 i = split_interval_left (i, len);
1466 /* We have to call set_properties even if we are going to
1467 merge the intervals, so as to make the undo records
1468 and cause redisplay to happen. */
1469 set_properties (properties, i, object);
1470 if (prev_changed)
1471 merge_interval_left (i);
1472 return;
1475 len -= LENGTH (i);
1477 /* We have to call set_properties even if we are going to
1478 merge the intervals, so as to make the undo records
1479 and cause redisplay to happen. */
1480 set_properties (properties, i, object);
1481 if (!prev_changed)
1482 prev_changed = i;
1483 else
1484 prev_changed = i = merge_interval_left (i);
1486 i = next_interval (i);
1488 while (len > 0);
1491 DEFUN ("remove-text-properties", Fremove_text_properties,
1492 Sremove_text_properties, 3, 4, 0,
1493 doc: /* Remove some properties from text from START to END.
1494 The third argument PROPERTIES is a property list
1495 whose property names specify the properties to remove.
1496 \(The values stored in PROPERTIES are ignored.)
1497 If the optional fourth argument OBJECT is a buffer (or nil, which means
1498 the current buffer), START and END are buffer positions (integers or
1499 markers). If OBJECT is a string, START and END are 0-based indices into it.
1500 Return t if any property was actually removed, nil otherwise.
1502 Use `set-text-properties' if you want to remove all text properties. */)
1503 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1505 INTERVAL i, unchanged;
1506 ptrdiff_t s, len;
1507 bool modified = false;
1508 bool first_time = true;
1510 if (NILP (object))
1511 XSETBUFFER (object, current_buffer);
1513 retry:
1514 i = validate_interval_range (object, &start, &end, soft);
1515 if (!i)
1516 return Qnil;
1518 s = XINT (start);
1519 len = XINT (end) - s;
1521 /* If there are no properties on this entire interval, return. */
1522 if (! interval_has_some_properties (properties, i))
1524 ptrdiff_t got = LENGTH (i) - (s - i->position);
1528 if (got >= len)
1529 return Qnil;
1530 len -= got;
1531 i = next_interval (i);
1532 got = LENGTH (i);
1534 while (! interval_has_some_properties (properties, i));
1536 /* Split away the beginning of this interval; what we don't
1537 want to modify. */
1538 else if (i->position != s)
1540 unchanged = i;
1541 i = split_interval_right (unchanged, s - unchanged->position);
1542 copy_properties (unchanged, i);
1545 if (BUFFERP (object) && first_time)
1547 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1548 ptrdiff_t prev_pos = i->position;
1550 modify_text_properties (object, start, end);
1551 /* If someone called us recursively as a side effect of
1552 modify_text_properties, and changed the intervals behind our back
1553 (could happen if lock_file, called by prepare_to_modify_buffer,
1554 triggers redisplay, and that calls add-text-properties again
1555 in the same buffer), we cannot continue with I, because its
1556 data changed. So we restart the interval analysis anew. */
1557 if (TOTAL_LENGTH (i) != prev_total_length
1558 || i->position != prev_pos)
1560 first_time = false;
1561 goto retry;
1565 /* We are at the beginning of an interval, with len to scan */
1566 for (;;)
1568 eassert (i != 0);
1570 if (LENGTH (i) >= len)
1572 if (! interval_has_some_properties (properties, i))
1574 eassert (modified);
1575 if (BUFFERP (object))
1576 signal_after_change (XINT (start), XINT (end) - XINT (start),
1577 XINT (end) - XINT (start));
1578 return Qt;
1581 if (LENGTH (i) == len)
1583 remove_properties (properties, Qnil, i, object);
1584 if (BUFFERP (object))
1585 signal_after_change (XINT (start), XINT (end) - XINT (start),
1586 XINT (end) - XINT (start));
1587 return Qt;
1590 /* i has the properties, and goes past the change limit */
1591 unchanged = i;
1592 i = split_interval_left (i, len);
1593 copy_properties (unchanged, i);
1594 remove_properties (properties, Qnil, i, object);
1595 if (BUFFERP (object))
1596 signal_after_change (XINT (start), XINT (end) - XINT (start),
1597 XINT (end) - XINT (start));
1598 return Qt;
1601 len -= LENGTH (i);
1602 modified |= remove_properties (properties, Qnil, i, object);
1603 i = next_interval (i);
1607 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1608 Sremove_list_of_text_properties, 3, 4, 0,
1609 doc: /* Remove some properties from text from START to END.
1610 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1611 If the optional fourth argument OBJECT is a buffer (or nil, which means
1612 the current buffer), START and END are buffer positions (integers or
1613 markers). If OBJECT is a string, START and END are 0-based indices into it.
1614 Return t if any property was actually removed, nil otherwise. */)
1615 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1617 INTERVAL i, unchanged;
1618 ptrdiff_t s, len;
1619 bool modified = false;
1620 Lisp_Object properties;
1621 properties = list_of_properties;
1623 if (NILP (object))
1624 XSETBUFFER (object, current_buffer);
1626 i = validate_interval_range (object, &start, &end, soft);
1627 if (!i)
1628 return Qnil;
1630 s = XINT (start);
1631 len = XINT (end) - s;
1633 /* If there are no properties on the interval, return. */
1634 if (! interval_has_some_properties_list (properties, i))
1636 ptrdiff_t got = LENGTH (i) - (s - i->position);
1640 if (got >= len)
1641 return Qnil;
1642 len -= got;
1643 i = next_interval (i);
1644 got = LENGTH (i);
1646 while (! interval_has_some_properties_list (properties, i));
1648 /* Split away the beginning of this interval; what we don't
1649 want to modify. */
1650 else if (i->position != s)
1652 unchanged = i;
1653 i = split_interval_right (unchanged, s - unchanged->position);
1654 copy_properties (unchanged, i);
1657 /* We are at the beginning of an interval, with len to scan.
1658 The flag MODIFIED records if changes have been made.
1659 When object is a buffer, we must call modify_text_properties
1660 before changes are made and signal_after_change when we are done.
1661 Call modify_text_properties before calling remove_properties if !MODIFIED,
1662 and call signal_after_change before returning if MODIFIED. */
1663 for (;;)
1665 eassert (i != 0);
1667 if (LENGTH (i) >= len)
1669 if (! interval_has_some_properties_list (properties, i))
1671 if (modified)
1673 if (BUFFERP (object))
1674 signal_after_change (XINT (start),
1675 XINT (end) - XINT (start),
1676 XINT (end) - XINT (start));
1677 return Qt;
1679 else
1680 return Qnil;
1682 else if (LENGTH (i) == len)
1684 if (!modified && BUFFERP (object))
1685 modify_text_properties (object, start, end);
1686 remove_properties (Qnil, properties, i, object);
1687 if (BUFFERP (object))
1688 signal_after_change (XINT (start), XINT (end) - XINT (start),
1689 XINT (end) - XINT (start));
1690 return Qt;
1692 else
1693 { /* i has the properties, and goes past the change limit. */
1694 unchanged = i;
1695 i = split_interval_left (i, len);
1696 copy_properties (unchanged, i);
1697 if (!modified && BUFFERP (object))
1698 modify_text_properties (object, start, end);
1699 remove_properties (Qnil, properties, i, object);
1700 if (BUFFERP (object))
1701 signal_after_change (XINT (start), XINT (end) - XINT (start),
1702 XINT (end) - XINT (start));
1703 return Qt;
1706 if (interval_has_some_properties_list (properties, i))
1708 if (!modified && BUFFERP (object))
1709 modify_text_properties (object, start, end);
1710 remove_properties (Qnil, properties, i, object);
1711 modified = true;
1713 len -= LENGTH (i);
1714 i = next_interval (i);
1715 if (!i)
1717 if (modified)
1719 if (BUFFERP (object))
1720 signal_after_change (XINT (start),
1721 XINT (end) - XINT (start),
1722 XINT (end) - XINT (start));
1723 return Qt;
1725 else
1726 return Qnil;
1731 DEFUN ("text-property-any", Ftext_property_any,
1732 Stext_property_any, 4, 5, 0,
1733 doc: /* Check text from START to END for property PROPERTY equaling VALUE.
1734 If so, return the position of the first character whose property PROPERTY
1735 is `eq' to VALUE. Otherwise return nil.
1736 If the optional fifth argument OBJECT is a buffer (or nil, which means
1737 the current buffer), START and END are buffer positions (integers or
1738 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1739 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1741 register INTERVAL i;
1742 register ptrdiff_t e, pos;
1744 if (NILP (object))
1745 XSETBUFFER (object, current_buffer);
1746 i = validate_interval_range (object, &start, &end, soft);
1747 if (!i)
1748 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1749 e = XINT (end);
1751 while (i)
1753 if (i->position >= e)
1754 break;
1755 if (EQ (textget (i->plist, property), value))
1757 pos = i->position;
1758 if (pos < XINT (start))
1759 pos = XINT (start);
1760 return make_number (pos);
1762 i = next_interval (i);
1764 return Qnil;
1767 DEFUN ("text-property-not-all", Ftext_property_not_all,
1768 Stext_property_not_all, 4, 5, 0,
1769 doc: /* Check text from START to END for property PROPERTY not equaling VALUE.
1770 If so, return the position of the first character whose property PROPERTY
1771 is not `eq' to VALUE. Otherwise, return nil.
1772 If the optional fifth argument OBJECT is a buffer (or nil, which means
1773 the current buffer), START and END are buffer positions (integers or
1774 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1775 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1777 register INTERVAL i;
1778 register ptrdiff_t s, e;
1780 if (NILP (object))
1781 XSETBUFFER (object, current_buffer);
1782 i = validate_interval_range (object, &start, &end, soft);
1783 if (!i)
1784 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1785 s = XINT (start);
1786 e = XINT (end);
1788 while (i)
1790 if (i->position >= e)
1791 break;
1792 if (! EQ (textget (i->plist, property), value))
1794 if (i->position > s)
1795 s = i->position;
1796 return make_number (s);
1798 i = next_interval (i);
1800 return Qnil;
1804 /* Return the direction from which the text-property PROP would be
1805 inherited by any new text inserted at POS: 1 if it would be
1806 inherited from the char after POS, -1 if it would be inherited from
1807 the char before POS, and 0 if from neither.
1808 BUFFER can be either a buffer or nil (meaning current buffer). */
1811 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1813 bool ignore_previous_character;
1814 Lisp_Object prev_pos = make_number (XINT (pos) - 1);
1815 Lisp_Object front_sticky;
1816 bool is_rear_sticky = true, is_front_sticky = false; /* defaults */
1817 Lisp_Object defalt = Fassq (prop, Vtext_property_default_nonsticky);
1819 if (NILP (buffer))
1820 XSETBUFFER (buffer, current_buffer);
1822 ignore_previous_character = XINT (pos) <= BUF_BEGV (XBUFFER (buffer));
1824 if (ignore_previous_character || (CONSP (defalt) && !NILP (XCDR (defalt))))
1825 is_rear_sticky = false;
1826 else
1828 Lisp_Object rear_non_sticky
1829 = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1831 if (!NILP (CONSP (rear_non_sticky)
1832 ? Fmemq (prop, rear_non_sticky)
1833 : rear_non_sticky))
1834 /* PROP is rear-non-sticky. */
1835 is_rear_sticky = false;
1838 /* Consider following character. */
1839 /* This signals an arg-out-of-range error if pos is outside the
1840 buffer's accessible range. */
1841 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1843 if (EQ (front_sticky, Qt)
1844 || (CONSP (front_sticky)
1845 && !NILP (Fmemq (prop, front_sticky))))
1846 /* PROP is inherited from after. */
1847 is_front_sticky = true;
1849 /* Simple cases, where the properties are consistent. */
1850 if (is_rear_sticky && !is_front_sticky)
1851 return -1;
1852 else if (!is_rear_sticky && is_front_sticky)
1853 return 1;
1854 else if (!is_rear_sticky && !is_front_sticky)
1855 return 0;
1857 /* The stickiness properties are inconsistent, so we have to
1858 disambiguate. Basically, rear-sticky wins, _except_ if the
1859 property that would be inherited has a value of nil, in which case
1860 front-sticky wins. */
1861 if (ignore_previous_character
1862 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1863 return 1;
1864 else
1865 return -1;
1869 /* Copying properties between objects. */
1871 /* Add properties from START to END of SRC, starting at POS in DEST.
1872 SRC and DEST may each refer to strings or buffers.
1873 Optional sixth argument PROP causes only that property to be copied.
1874 Properties are copied to DEST as if by `add-text-properties'.
1875 Return t if any property value actually changed, nil otherwise. */
1877 /* Note this can GC when DEST is a buffer. */
1879 Lisp_Object
1880 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src,
1881 Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1883 INTERVAL i;
1884 Lisp_Object res;
1885 Lisp_Object stuff;
1886 Lisp_Object plist;
1887 ptrdiff_t s, e, e2, p, len;
1888 bool modified = false;
1890 i = validate_interval_range (src, &start, &end, soft);
1891 if (!i)
1892 return Qnil;
1894 CHECK_NUMBER_COERCE_MARKER (pos);
1896 Lisp_Object dest_start, dest_end;
1898 e = XINT (pos) + (XINT (end) - XINT (start));
1899 if (MOST_POSITIVE_FIXNUM < e)
1900 args_out_of_range (pos, end);
1901 dest_start = pos;
1902 XSETFASTINT (dest_end, e);
1903 /* Apply this to a copy of pos; it will try to increment its arguments,
1904 which we don't want. */
1905 validate_interval_range (dest, &dest_start, &dest_end, soft);
1908 s = XINT (start);
1909 e = XINT (end);
1910 p = XINT (pos);
1912 stuff = Qnil;
1914 while (s < e)
1916 e2 = i->position + LENGTH (i);
1917 if (e2 > e)
1918 e2 = e;
1919 len = e2 - s;
1921 plist = i->plist;
1922 if (! NILP (prop))
1923 while (! NILP (plist))
1925 if (EQ (Fcar (plist), prop))
1927 plist = list2 (prop, Fcar (Fcdr (plist)));
1928 break;
1930 plist = Fcdr (Fcdr (plist));
1932 if (! NILP (plist))
1933 /* Must defer modifications to the interval tree in case
1934 src and dest refer to the same string or buffer. */
1935 stuff = Fcons (list3 (make_number (p), make_number (p + len), plist),
1936 stuff);
1938 i = next_interval (i);
1939 if (!i)
1940 break;
1942 p += len;
1943 s = i->position;
1946 while (! NILP (stuff))
1948 res = Fcar (stuff);
1949 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1950 Fcar (Fcdr (Fcdr (res))), dest);
1951 if (! NILP (res))
1952 modified = true;
1953 stuff = Fcdr (stuff);
1956 return modified ? Qt : Qnil;
1960 /* Return a list representing the text properties of OBJECT between
1961 START and END. if PROP is non-nil, report only on that property.
1962 Each result list element has the form (S E PLIST), where S and E
1963 are positions in OBJECT and PLIST is a property list containing the
1964 text properties of OBJECT between S and E. Value is nil if OBJECT
1965 doesn't contain text properties between START and END. */
1967 Lisp_Object
1968 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1970 struct interval *i;
1971 Lisp_Object result;
1973 result = Qnil;
1975 i = validate_interval_range (object, &start, &end, soft);
1976 if (i)
1978 ptrdiff_t s = XINT (start);
1979 ptrdiff_t e = XINT (end);
1981 while (s < e)
1983 ptrdiff_t interval_end, len;
1984 Lisp_Object plist;
1986 interval_end = i->position + LENGTH (i);
1987 if (interval_end > e)
1988 interval_end = e;
1989 len = interval_end - s;
1991 plist = i->plist;
1993 if (!NILP (prop))
1994 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1995 if (EQ (XCAR (plist), prop))
1997 plist = list2 (prop, Fcar (XCDR (plist)));
1998 break;
2001 if (!NILP (plist))
2002 result = Fcons (list3 (make_number (s), make_number (s + len),
2003 plist),
2004 result);
2006 i = next_interval (i);
2007 if (!i)
2008 break;
2009 s = i->position;
2013 return result;
2017 /* Add text properties to OBJECT from LIST. LIST is a list of triples
2018 (START END PLIST), where START and END are positions and PLIST is a
2019 property list containing the text properties to add. Adjust START
2020 and END positions by DELTA before adding properties. */
2022 void
2023 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
2025 for (; CONSP (list); list = XCDR (list))
2027 Lisp_Object item, start, end, plist;
2029 item = XCAR (list);
2030 start = make_number (XINT (XCAR (item)) + XINT (delta));
2031 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
2032 plist = XCAR (XCDR (XCDR (item)));
2034 Fadd_text_properties (start, end, plist, object);
2040 /* Modify end-points of ranges in LIST destructively, and return the
2041 new list. LIST is a list as returned from text_property_list.
2042 Discard properties that begin at or after NEW_END, and limit
2043 end-points to NEW_END. */
2045 Lisp_Object
2046 extend_property_ranges (Lisp_Object list, Lisp_Object old_end, Lisp_Object new_end)
2048 Lisp_Object prev = Qnil, head = list;
2049 ptrdiff_t max = XINT (new_end);
2051 for (; CONSP (list); prev = list, list = XCDR (list))
2053 Lisp_Object item, beg;
2054 ptrdiff_t end;
2056 item = XCAR (list);
2057 beg = XCAR (item);
2058 end = XINT (XCAR (XCDR (item)));
2060 if (XINT (beg) >= max)
2062 /* The start-point is past the end of the new string.
2063 Discard this property. */
2064 if (EQ (head, list))
2065 head = XCDR (list);
2066 else
2067 XSETCDR (prev, XCDR (list));
2069 else if ((end == XINT (old_end) && end != max)
2070 || end > max)
2072 /* Either the end-point is past the end of the new string,
2073 and we need to discard the properties past the new end,
2074 or the caller is extending the property range, and we
2075 should update all end-points that are on the old end of
2076 the range to reflect that. */
2077 XSETCAR (XCDR (item), new_end);
2081 return head;
2086 /* Call the modification hook functions in LIST, each with START and END. */
2088 static void
2089 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2091 while (!NILP (list))
2093 call2 (Fcar (list), start, end);
2094 list = Fcdr (list);
2098 /* Check for read-only intervals between character positions START ... END,
2099 in BUF, and signal an error if we find one.
2101 Then check for any modification hooks in the range.
2102 Create a list of all these hooks in lexicographic order,
2103 eliminating consecutive extra copies of the same hook. Then call
2104 those hooks in order, with START and END - 1 as arguments. */
2106 void
2107 verify_interval_modification (struct buffer *buf,
2108 ptrdiff_t start, ptrdiff_t end)
2110 INTERVAL intervals = buffer_intervals (buf);
2111 INTERVAL i;
2112 Lisp_Object hooks;
2113 Lisp_Object prev_mod_hooks;
2114 Lisp_Object mod_hooks;
2116 hooks = Qnil;
2117 prev_mod_hooks = Qnil;
2118 mod_hooks = Qnil;
2120 interval_insert_behind_hooks = Qnil;
2121 interval_insert_in_front_hooks = Qnil;
2123 if (!intervals)
2124 return;
2126 if (start > end)
2128 ptrdiff_t temp = start;
2129 start = end;
2130 end = temp;
2133 /* For an insert operation, check the two chars around the position. */
2134 if (start == end)
2136 INTERVAL prev = NULL;
2137 Lisp_Object before, after;
2139 /* Set I to the interval containing the char after START,
2140 and PREV to the interval containing the char before START.
2141 Either one may be null. They may be equal. */
2142 i = find_interval (intervals, start);
2144 if (start == BUF_BEGV (buf))
2145 prev = 0;
2146 else if (i->position == start)
2147 prev = previous_interval (i);
2148 else if (i->position < start)
2149 prev = i;
2150 if (start == BUF_ZV (buf))
2151 i = 0;
2153 /* If Vinhibit_read_only is set and is not a list, we can
2154 skip the read_only checks. */
2155 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2157 /* If I and PREV differ we need to check for the read-only
2158 property together with its stickiness. If either I or
2159 PREV are 0, this check is all we need.
2160 We have to take special care, since read-only may be
2161 indirectly defined via the category property. */
2162 if (i != prev)
2164 if (i)
2166 after = textget (i->plist, Qread_only);
2168 /* If interval I is read-only and read-only is
2169 front-sticky, inhibit insertion.
2170 Check for read-only as well as category. */
2171 if (! NILP (after)
2172 && NILP (Fmemq (after, Vinhibit_read_only)))
2174 Lisp_Object tem;
2176 tem = textget (i->plist, Qfront_sticky);
2177 if (TMEM (Qread_only, tem)
2178 || (NILP (Fplist_get (i->plist, Qread_only))
2179 && TMEM (Qcategory, tem)))
2180 text_read_only (after);
2184 if (prev)
2186 before = textget (prev->plist, Qread_only);
2188 /* If interval PREV is read-only and read-only isn't
2189 rear-nonsticky, inhibit insertion.
2190 Check for read-only as well as category. */
2191 if (! NILP (before)
2192 && NILP (Fmemq (before, Vinhibit_read_only)))
2194 Lisp_Object tem;
2196 tem = textget (prev->plist, Qrear_nonsticky);
2197 if (! TMEM (Qread_only, tem)
2198 && (! NILP (Fplist_get (prev->plist,Qread_only))
2199 || ! TMEM (Qcategory, tem)))
2200 text_read_only (before);
2204 else if (i)
2206 after = textget (i->plist, Qread_only);
2208 /* If interval I is read-only and read-only is
2209 front-sticky, inhibit insertion.
2210 Check for read-only as well as category. */
2211 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2213 Lisp_Object tem;
2215 tem = textget (i->plist, Qfront_sticky);
2216 if (TMEM (Qread_only, tem)
2217 || (NILP (Fplist_get (i->plist, Qread_only))
2218 && TMEM (Qcategory, tem)))
2219 text_read_only (after);
2221 tem = textget (prev->plist, Qrear_nonsticky);
2222 if (! TMEM (Qread_only, tem)
2223 && (! NILP (Fplist_get (prev->plist, Qread_only))
2224 || ! TMEM (Qcategory, tem)))
2225 text_read_only (after);
2230 /* Run both insert hooks (just once if they're the same). */
2231 if (prev)
2232 interval_insert_behind_hooks
2233 = textget (prev->plist, Qinsert_behind_hooks);
2234 if (i)
2235 interval_insert_in_front_hooks
2236 = textget (i->plist, Qinsert_in_front_hooks);
2238 else
2240 /* Loop over intervals on or next to START...END,
2241 collecting their hooks. */
2243 i = find_interval (intervals, start);
2246 if (! INTERVAL_WRITABLE_P (i))
2247 text_read_only (textget (i->plist, Qread_only));
2249 if (!inhibit_modification_hooks)
2251 mod_hooks = textget (i->plist, Qmodification_hooks);
2252 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2254 hooks = Fcons (mod_hooks, hooks);
2255 prev_mod_hooks = mod_hooks;
2259 if (i->position + LENGTH (i) < end
2260 && (!NILP (BVAR (current_buffer, read_only))
2261 && NILP (Vinhibit_read_only)))
2262 xsignal1 (Qbuffer_read_only, Fcurrent_buffer ());
2264 i = next_interval (i);
2266 /* Keep going thru the interval containing the char before END. */
2267 while (i && i->position < end);
2269 if (!inhibit_modification_hooks)
2271 hooks = Fnreverse (hooks);
2272 while (! EQ (hooks, Qnil))
2274 call_mod_hooks (Fcar (hooks), make_number (start),
2275 make_number (end));
2276 hooks = Fcdr (hooks);
2282 /* Run the interval hooks for an insertion on character range START ... END.
2283 verify_interval_modification chose which hooks to run;
2284 this function is called after the insertion happens
2285 so it can indicate the range of inserted text. */
2287 void
2288 report_interval_modification (Lisp_Object start, Lisp_Object end)
2290 if (! NILP (interval_insert_behind_hooks))
2291 call_mod_hooks (interval_insert_behind_hooks, start, end);
2292 if (! NILP (interval_insert_in_front_hooks)
2293 && ! EQ (interval_insert_in_front_hooks,
2294 interval_insert_behind_hooks))
2295 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2298 void
2299 syms_of_textprop (void)
2301 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties,
2302 doc: /* Property-list used as default values.
2303 The value of a property in this list is seen as the value for every
2304 character that does not have its own value for that property. */);
2305 Vdefault_text_properties = Qnil;
2307 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist,
2308 doc: /* Alist of alternative properties for properties without a value.
2309 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2310 If a piece of text has no direct value for a particular property, then
2311 this alist is consulted. If that property appears in the alist, then
2312 the first non-nil value from the associated alternative properties is
2313 returned. */);
2314 Vchar_property_alias_alist = Qnil;
2316 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks,
2317 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2318 This also inhibits the use of the `intangible' text property.
2320 This variable is obsolete since Emacs-25.1. Use `cursor-intangible-mode'
2321 or `cursor-sensor-mode' instead. */);
2322 /* FIXME: We should make-obsolete-variable, but that signals too many
2323 warnings in code which does (let ((inhibit-point-motion-hooks t)) ...)
2324 Ideally, make-obsolete-variable should let us specify that only the nil
2325 value is obsolete, but that requires too many changes in bytecomp.el,
2326 so for now we'll keep it "obsolete via the docstring". */
2327 Vinhibit_point_motion_hooks = Qt;
2329 DEFVAR_LISP ("text-property-default-nonsticky",
2330 Vtext_property_default_nonsticky,
2331 doc: /* Alist of properties vs the corresponding non-stickiness.
2332 Each element has the form (PROPERTY . NONSTICKINESS).
2334 If a character in a buffer has PROPERTY, new text inserted adjacent to
2335 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2336 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2337 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2338 /* Text properties `syntax-table'and `display' should be nonsticky
2339 by default. */
2340 Vtext_property_default_nonsticky
2341 = list2 (Fcons (Qsyntax_table, Qt), Fcons (Qdisplay, Qt));
2343 staticpro (&interval_insert_behind_hooks);
2344 staticpro (&interval_insert_in_front_hooks);
2345 interval_insert_behind_hooks = Qnil;
2346 interval_insert_in_front_hooks = Qnil;
2349 /* Common attributes one might give text. */
2351 DEFSYM (Qfont, "font");
2352 DEFSYM (Qface, "face");
2353 DEFSYM (Qread_only, "read-only");
2354 DEFSYM (Qinvisible, "invisible");
2355 DEFSYM (Qintangible, "intangible");
2356 DEFSYM (Qcategory, "category");
2357 DEFSYM (Qlocal_map, "local-map");
2358 DEFSYM (Qfront_sticky, "front-sticky");
2359 DEFSYM (Qrear_nonsticky, "rear-nonsticky");
2360 DEFSYM (Qmouse_face, "mouse-face");
2361 DEFSYM (Qminibuffer_prompt, "minibuffer-prompt");
2363 /* Properties that text might use to specify certain actions. */
2365 DEFSYM (Qpoint_left, "point-left");
2366 DEFSYM (Qpoint_entered, "point-entered");
2368 defsubr (&Stext_properties_at);
2369 defsubr (&Sget_text_property);
2370 defsubr (&Sget_char_property);
2371 defsubr (&Sget_char_property_and_overlay);
2372 defsubr (&Snext_char_property_change);
2373 defsubr (&Sprevious_char_property_change);
2374 defsubr (&Snext_single_char_property_change);
2375 defsubr (&Sprevious_single_char_property_change);
2376 defsubr (&Snext_property_change);
2377 defsubr (&Snext_single_property_change);
2378 defsubr (&Sprevious_property_change);
2379 defsubr (&Sprevious_single_property_change);
2380 defsubr (&Sadd_text_properties);
2381 defsubr (&Sput_text_property);
2382 defsubr (&Sset_text_properties);
2383 defsubr (&Sadd_face_text_property);
2384 defsubr (&Sremove_text_properties);
2385 defsubr (&Sremove_list_of_text_properties);
2386 defsubr (&Stext_property_any);
2387 defsubr (&Stext_property_not_all);