Add separator.pbm tool-bar image
[emacs.git] / src / textprop.c
blob3f7c8d106395e9b446946eec56a880a83ab647a7
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993-1995, 1997, 1999-2015 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. */
48 enum property_set_type
50 TEXT_PROPERTY_REPLACE,
51 TEXT_PROPERTY_PREPEND,
52 TEXT_PROPERTY_APPEND
55 /* If o1 is a cons whose cdr is a cons, return true and set o2 to
56 the o1's cdr. Otherwise, return false. This is handy for
57 traversing plists. */
58 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
60 /* verify_interval_modification saves insertion hooks here
61 to be run later by report_interval_modification. */
62 static Lisp_Object interval_insert_behind_hooks;
63 static Lisp_Object interval_insert_in_front_hooks;
66 /* Signal a `text-read-only' error. This function makes it easier
67 to capture that error in GDB by putting a breakpoint on it. */
69 static _Noreturn void
70 text_read_only (Lisp_Object propval)
72 if (STRINGP (propval))
73 xsignal1 (Qtext_read_only, propval);
75 xsignal0 (Qtext_read_only);
78 /* Prepare to modify the text properties of BUFFER from START to END. */
80 static void
81 modify_text_properties (Lisp_Object buffer, Lisp_Object start, Lisp_Object end)
83 ptrdiff_t b = XINT (start), e = XINT (end);
84 struct buffer *buf = XBUFFER (buffer), *old = current_buffer;
86 set_buffer_internal (buf);
88 prepare_to_modify_buffer_1 (b, e, NULL);
90 BUF_COMPUTE_UNCHANGED (buf, b - 1, e);
91 if (MODIFF <= SAVE_MODIFF)
92 record_first_change ();
93 MODIFF++;
95 bset_point_before_scroll (current_buffer, Qnil);
97 set_buffer_internal (old);
100 /* Complain if object is not string or buffer type. */
102 static void
103 CHECK_STRING_OR_BUFFER (Lisp_Object x)
105 CHECK_TYPE (STRINGP (x) || BUFFERP (x), Qbuffer_or_string_p, x);
108 /* Extract the interval at the position pointed to by BEGIN from
109 OBJECT, a string or buffer. Additionally, check that the positions
110 pointed to by BEGIN and END are within the bounds of OBJECT, and
111 reverse them if *BEGIN is greater than *END. The objects pointed
112 to by BEGIN and END may be integers or markers; if the latter, they
113 are coerced to integers.
115 When OBJECT is a string, we increment *BEGIN and *END
116 to make them origin-one.
118 Note that buffer points don't correspond to interval indices.
119 For example, point-max is 1 greater than the index of the last
120 character. This difference is handled in the caller, which uses
121 the validated points to determine a length, and operates on that.
122 Exceptions are Ftext_properties_at, Fnext_property_change, and
123 Fprevious_property_change which call this function with BEGIN == END.
124 Handle this case specially.
126 If FORCE is soft (false), it's OK to return NULL. Otherwise,
127 create an interval tree for OBJECT if one doesn't exist, provided
128 the object actually contains text. In the current design, if there
129 is no text, there can be no text properties. */
131 enum { soft = false, hard = true };
133 INTERVAL
134 validate_interval_range (Lisp_Object object, Lisp_Object *begin,
135 Lisp_Object *end, bool force)
137 INTERVAL i;
138 ptrdiff_t searchpos;
140 CHECK_STRING_OR_BUFFER (object);
141 CHECK_NUMBER_COERCE_MARKER (*begin);
142 CHECK_NUMBER_COERCE_MARKER (*end);
144 /* If we are asked for a point, but from a subr which operates
145 on a range, then return nothing. */
146 if (EQ (*begin, *end) && begin != end)
147 return NULL;
149 if (XINT (*begin) > XINT (*end))
151 Lisp_Object n;
152 n = *begin;
153 *begin = *end;
154 *end = n;
157 if (BUFFERP (object))
159 register struct buffer *b = XBUFFER (object);
161 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
162 && XINT (*end) <= BUF_ZV (b)))
163 args_out_of_range (*begin, *end);
164 i = buffer_intervals (b);
166 /* If there's no text, there are no properties. */
167 if (BUF_BEGV (b) == BUF_ZV (b))
168 return NULL;
170 searchpos = XINT (*begin);
172 else
174 ptrdiff_t len = SCHARS (object);
176 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
177 && XINT (*end) <= len))
178 args_out_of_range (*begin, *end);
179 XSETFASTINT (*begin, XFASTINT (*begin));
180 if (begin != end)
181 XSETFASTINT (*end, XFASTINT (*end));
182 i = string_intervals (object);
184 if (len == 0)
185 return NULL;
187 searchpos = XINT (*begin);
190 if (!i)
191 return (force ? create_root_interval (object) : i);
193 return find_interval (i, searchpos);
196 /* Validate LIST as a property list. If LIST is not a list, then
197 make one consisting of (LIST nil). Otherwise, verify that LIST
198 is even numbered and thus suitable as a plist. */
200 static Lisp_Object
201 validate_plist (Lisp_Object list)
203 if (NILP (list))
204 return Qnil;
206 if (CONSP (list))
208 Lisp_Object tail = list;
211 tail = XCDR (tail);
212 if (! CONSP (tail))
213 error ("Odd length text property list");
214 tail = XCDR (tail);
215 QUIT;
217 while (CONSP (tail));
219 return list;
222 return list2 (list, Qnil);
225 /* Return true if interval I has all the properties,
226 with the same values, of list PLIST. */
228 static bool
229 interval_has_all_properties (Lisp_Object plist, INTERVAL i)
231 Lisp_Object tail1, tail2;
233 /* Go through each element of PLIST. */
234 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
236 Lisp_Object sym1 = XCAR (tail1);
237 bool found = false;
239 /* Go through I's plist, looking for sym1 */
240 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
241 if (EQ (sym1, XCAR (tail2)))
243 /* Found the same property on both lists. If the
244 values are unequal, return false. */
245 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
246 return false;
248 /* Property has same value on both lists; go to next one. */
249 found = true;
250 break;
253 if (! found)
254 return false;
257 return true;
260 /* Return true if the plist of interval I has any of the
261 properties of PLIST, regardless of their values. */
263 static bool
264 interval_has_some_properties (Lisp_Object plist, INTERVAL i)
266 Lisp_Object tail1, tail2, sym;
268 /* Go through each element of PLIST. */
269 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
271 sym = XCAR (tail1);
273 /* Go through i's plist, looking for tail1 */
274 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
275 if (EQ (sym, XCAR (tail2)))
276 return true;
279 return false;
282 /* Return true if the plist of interval I has any of the
283 property names in LIST, regardless of their values. */
285 static bool
286 interval_has_some_properties_list (Lisp_Object list, INTERVAL i)
288 Lisp_Object tail1, tail2, sym;
290 /* Go through each element of LIST. */
291 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
293 sym = XCAR (tail1);
295 /* Go through i's plist, looking for tail1 */
296 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
297 if (EQ (sym, XCAR (tail2)))
298 return true;
301 return false;
304 /* Changing the plists of individual intervals. */
306 /* Return the value of PROP in property-list PLIST, or Qunbound if it
307 has none. */
308 static Lisp_Object
309 property_value (Lisp_Object plist, Lisp_Object prop)
311 Lisp_Object value;
313 while (PLIST_ELT_P (plist, value))
314 if (EQ (XCAR (plist), prop))
315 return XCAR (value);
316 else
317 plist = XCDR (value);
319 return Qunbound;
322 /* Set the properties of INTERVAL to PROPERTIES,
323 and record undo info for the previous values.
324 OBJECT is the string or buffer that INTERVAL belongs to. */
326 static void
327 set_properties (Lisp_Object properties, INTERVAL interval, Lisp_Object object)
329 Lisp_Object sym, value;
331 if (BUFFERP (object))
333 /* For each property in the old plist which is missing from PROPERTIES,
334 or has a different value in PROPERTIES, make an undo record. */
335 for (sym = interval->plist;
336 PLIST_ELT_P (sym, value);
337 sym = XCDR (value))
338 if (! EQ (property_value (properties, XCAR (sym)),
339 XCAR (value)))
341 record_property_change (interval->position, LENGTH (interval),
342 XCAR (sym), XCAR (value),
343 object);
346 /* For each new property that has no value at all in the old plist,
347 make an undo record binding it to nil, so it will be removed. */
348 for (sym = properties;
349 PLIST_ELT_P (sym, value);
350 sym = XCDR (value))
351 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
353 record_property_change (interval->position, LENGTH (interval),
354 XCAR (sym), Qnil,
355 object);
359 /* Store new properties. */
360 set_interval_plist (interval, Fcopy_sequence (properties));
363 /* Add the properties of PLIST to the interval I, or set
364 the value of I's property to the value of the property on PLIST
365 if they are different.
367 OBJECT should be the string or buffer the interval is in.
369 Return true if this changes I (i.e., if any members of PLIST
370 are actually added to I's plist) */
372 static bool
373 add_properties (Lisp_Object plist, INTERVAL i, Lisp_Object object,
374 enum property_set_type set_type)
376 Lisp_Object tail1, tail2, sym1, val1;
377 bool changed = false;
379 tail1 = plist;
380 sym1 = Qnil;
381 val1 = Qnil;
383 /* Go through each element of PLIST. */
384 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
386 bool found = false;
387 sym1 = XCAR (tail1);
388 val1 = Fcar (XCDR (tail1));
390 /* Go through I's plist, looking for sym1 */
391 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
392 if (EQ (sym1, XCAR (tail2)))
394 Lisp_Object this_cdr;
396 this_cdr = XCDR (tail2);
397 /* Found the property. Now check its value. */
398 found = true;
400 /* The properties have the same value on both lists.
401 Continue to the next property. */
402 if (EQ (val1, Fcar (this_cdr)))
403 break;
405 /* Record this change in the buffer, for undo purposes. */
406 if (BUFFERP (object))
408 record_property_change (i->position, LENGTH (i),
409 sym1, Fcar (this_cdr), object);
412 /* I's property has a different value -- change it */
413 if (set_type == TEXT_PROPERTY_REPLACE)
414 Fsetcar (this_cdr, val1);
415 else {
416 if (CONSP (Fcar (this_cdr)) &&
417 /* Special-case anonymous face properties. */
418 (! EQ (sym1, Qface) ||
419 NILP (Fkeywordp (Fcar (Fcar (this_cdr))))))
420 /* The previous value is a list, so prepend (or
421 append) the new value to this list. */
422 if (set_type == TEXT_PROPERTY_PREPEND)
423 Fsetcar (this_cdr, Fcons (val1, Fcar (this_cdr)));
424 else
425 nconc2 (Fcar (this_cdr), list1 (val1));
426 else {
427 /* The previous value is a single value, so make it
428 into a list. */
429 if (set_type == TEXT_PROPERTY_PREPEND)
430 Fsetcar (this_cdr, list2 (val1, Fcar (this_cdr)));
431 else
432 Fsetcar (this_cdr, list2 (Fcar (this_cdr), val1));
435 changed = true;
436 break;
439 if (! found)
441 /* Record this change in the buffer, for undo purposes. */
442 if (BUFFERP (object))
444 record_property_change (i->position, LENGTH (i),
445 sym1, Qnil, object);
447 set_interval_plist (i, Fcons (sym1, Fcons (val1, i->plist)));
448 changed = true;
452 return changed;
455 /* For any members of PLIST, or LIST,
456 which are properties of I, remove them from I's plist.
457 (If PLIST is non-nil, use that, otherwise use LIST.)
458 OBJECT is the string or buffer containing I. */
460 static bool
461 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
463 bool changed = false;
465 /* True means tail1 is a plist, otherwise it is a list. */
466 bool use_plist = ! NILP (plist);
467 Lisp_Object tail1 = use_plist ? plist : list;
469 Lisp_Object current_plist = i->plist;
471 /* Go through each element of LIST or PLIST. */
472 while (CONSP (tail1))
474 Lisp_Object sym = XCAR (tail1);
476 /* First, remove the symbol if it's at the head of the list */
477 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
479 if (BUFFERP (object))
480 record_property_change (i->position, LENGTH (i),
481 sym, XCAR (XCDR (current_plist)),
482 object);
484 current_plist = XCDR (XCDR (current_plist));
485 changed = true;
488 /* Go through I's plist, looking for SYM. */
489 Lisp_Object tail2 = current_plist;
490 while (! NILP (tail2))
492 Lisp_Object this = XCDR (XCDR (tail2));
493 if (CONSP (this) && EQ (sym, XCAR (this)))
495 if (BUFFERP (object))
496 record_property_change (i->position, LENGTH (i),
497 sym, XCAR (XCDR (this)), object);
499 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
500 changed = true;
502 tail2 = this;
505 /* Advance thru TAIL1 one way or the other. */
506 tail1 = XCDR (tail1);
507 if (use_plist && CONSP (tail1))
508 tail1 = XCDR (tail1);
511 if (changed)
512 set_interval_plist (i, current_plist);
513 return changed;
516 /* Returns the interval of POSITION in OBJECT.
517 POSITION is BEG-based. */
519 INTERVAL
520 interval_of (ptrdiff_t position, Lisp_Object object)
522 register INTERVAL i;
523 ptrdiff_t beg, end;
525 if (NILP (object))
526 XSETBUFFER (object, current_buffer);
527 else if (EQ (object, Qt))
528 return NULL;
530 CHECK_STRING_OR_BUFFER (object);
532 if (BUFFERP (object))
534 register struct buffer *b = XBUFFER (object);
536 beg = BUF_BEGV (b);
537 end = BUF_ZV (b);
538 i = buffer_intervals (b);
540 else
542 beg = 0;
543 end = SCHARS (object);
544 i = string_intervals (object);
547 if (!(beg <= position && position <= end))
548 args_out_of_range (make_number (position), make_number (position));
549 if (beg == end || !i)
550 return NULL;
552 return find_interval (i, position);
555 DEFUN ("text-properties-at", Ftext_properties_at,
556 Stext_properties_at, 1, 2, 0,
557 doc: /* Return the list of properties of the character at POSITION in OBJECT.
558 If the optional second argument OBJECT is a buffer (or nil, which means
559 the current buffer), POSITION is a buffer position (integer or marker).
560 If OBJECT is a string, POSITION is a 0-based index into it.
561 If POSITION is at the end of OBJECT, the value is nil. */)
562 (Lisp_Object position, Lisp_Object object)
564 register INTERVAL i;
566 if (NILP (object))
567 XSETBUFFER (object, current_buffer);
569 i = validate_interval_range (object, &position, &position, soft);
570 if (!i)
571 return Qnil;
572 /* If POSITION is at the end of the interval,
573 it means it's the end of OBJECT.
574 There are no properties at the very end,
575 since no character follows. */
576 if (XINT (position) == LENGTH (i) + i->position)
577 return Qnil;
579 return i->plist;
582 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
583 doc: /* Return the value of POSITION's property PROP, in OBJECT.
584 OBJECT should be a buffer or a string; if omitted or nil, it defaults
585 to the current buffer.
586 If POSITION is at the end of OBJECT, the value is nil. */)
587 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
589 return textget (Ftext_properties_at (position, object), prop);
592 /* Return the value of char's property PROP, in OBJECT at POSITION.
593 OBJECT is optional and defaults to the current buffer.
594 If OVERLAY is non-0, then in the case that the returned property is from
595 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
596 returned in *OVERLAY.
597 If POSITION is at the end of OBJECT, the value is nil.
598 If OBJECT is a buffer, then overlay properties are considered as well as
599 text properties.
600 If OBJECT is a window, then that window's buffer is used, but
601 window-specific overlays are considered only if they are associated
602 with OBJECT. */
603 Lisp_Object
604 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
606 struct window *w = 0;
608 CHECK_NUMBER_COERCE_MARKER (position);
610 if (NILP (object))
611 XSETBUFFER (object, current_buffer);
613 if (WINDOWP (object))
615 CHECK_LIVE_WINDOW (object);
616 w = XWINDOW (object);
617 object = w->contents;
619 if (BUFFERP (object))
621 ptrdiff_t noverlays;
622 Lisp_Object *overlay_vec;
623 struct buffer *obuf = current_buffer;
625 if (XINT (position) < BUF_BEGV (XBUFFER (object))
626 || XINT (position) > BUF_ZV (XBUFFER (object)))
627 xsignal1 (Qargs_out_of_range, position);
629 set_buffer_temp (XBUFFER (object));
631 USE_SAFE_ALLOCA;
632 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, false);
633 noverlays = sort_overlays (overlay_vec, noverlays, w);
635 set_buffer_temp (obuf);
637 /* Now check the overlays in order of decreasing priority. */
638 while (--noverlays >= 0)
640 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
641 if (!NILP (tem))
643 if (overlay)
644 /* Return the overlay we got the property from. */
645 *overlay = overlay_vec[noverlays];
646 SAFE_FREE ();
647 return tem;
650 SAFE_FREE ();
653 if (overlay)
654 /* Indicate that the return value is not from an overlay. */
655 *overlay = Qnil;
657 /* Not a buffer, or no appropriate overlay, so fall through to the
658 simpler case. */
659 return Fget_text_property (position, prop, object);
662 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
663 doc: /* Return the value of POSITION's property PROP, in OBJECT.
664 Both overlay properties and text properties are checked.
665 OBJECT is optional and defaults to the current buffer.
666 If POSITION is at the end of OBJECT, the value is nil.
667 If OBJECT is a buffer, then overlay properties are considered as well as
668 text properties.
669 If OBJECT is a window, then that window's buffer is used, but window-specific
670 overlays are considered only if they are associated with OBJECT. */)
671 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
673 return get_char_property_and_overlay (position, prop, object, 0);
676 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
677 Sget_char_property_and_overlay, 2, 3, 0,
678 doc: /* Like `get-char-property', but with extra overlay information.
679 The value is a cons cell. Its car is the return value of `get-char-property'
680 with the same arguments--that is, the value of POSITION's property
681 PROP in OBJECT. Its cdr is the overlay in which the property was
682 found, or nil, if it was found as a text property or not found at all.
684 OBJECT is optional and defaults to the current buffer. OBJECT may be
685 a string, a buffer or a window. For strings, the cdr of the return
686 value is always nil, since strings do not have overlays. If OBJECT is
687 a window, then that window's buffer is used, but window-specific
688 overlays are considered only if they are associated with OBJECT. If
689 POSITION is at the end of OBJECT, both car and cdr are nil. */)
690 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
692 Lisp_Object overlay;
693 Lisp_Object val
694 = get_char_property_and_overlay (position, prop, object, &overlay);
695 return Fcons (val, overlay);
699 DEFUN ("next-char-property-change", Fnext_char_property_change,
700 Snext_char_property_change, 1, 2, 0,
701 doc: /* Return the position of next text property or overlay change.
702 This scans characters forward in the current buffer from POSITION till
703 it finds a change in some text property, or the beginning or end of an
704 overlay, and returns the position of that.
705 If none is found, and LIMIT is nil or omitted, the function
706 returns (point-max).
708 If the optional second argument LIMIT is non-nil, the function doesn't
709 search past position LIMIT, and returns LIMIT if nothing is found
710 before LIMIT. LIMIT is a no-op if it is greater than (point-max). */)
711 (Lisp_Object position, Lisp_Object limit)
713 Lisp_Object temp;
715 temp = Fnext_overlay_change (position);
716 if (! NILP (limit))
718 CHECK_NUMBER_COERCE_MARKER (limit);
719 if (XINT (limit) < XINT (temp))
720 temp = limit;
722 return Fnext_property_change (position, Qnil, temp);
725 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
726 Sprevious_char_property_change, 1, 2, 0,
727 doc: /* Return the position of previous text property or overlay change.
728 Scans characters backward in the current buffer from POSITION till it
729 finds a change in some text property, or the beginning or end of an
730 overlay, and returns the position of that.
731 If none is found, and LIMIT is nil or omitted, the function
732 returns (point-min).
734 If the optional second argument LIMIT is non-nil, the function doesn't
735 search before position LIMIT, and returns LIMIT if nothing is found
736 before LIMIT. LIMIT is a no-op if it is less than (point-min). */)
737 (Lisp_Object position, Lisp_Object limit)
739 Lisp_Object temp;
741 temp = Fprevious_overlay_change (position);
742 if (! NILP (limit))
744 CHECK_NUMBER_COERCE_MARKER (limit);
745 if (XINT (limit) > XINT (temp))
746 temp = limit;
748 return Fprevious_property_change (position, Qnil, temp);
752 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
753 Snext_single_char_property_change, 2, 4, 0,
754 doc: /* Return the position of next text property or overlay change for a specific property.
755 Scans characters forward from POSITION till it finds
756 a change in the PROP property, then returns the position of the change.
757 If the optional third argument OBJECT is a buffer (or nil, which means
758 the current buffer), POSITION is a buffer position (integer or marker).
759 If OBJECT is a string, POSITION is a 0-based index into it.
761 In a string, scan runs to the end of the string, unless LIMIT is non-nil.
762 In a buffer, if LIMIT is nil or omitted, it runs to (point-max), and the
763 value cannot exceed that.
764 If the optional fourth argument LIMIT is non-nil, don't search
765 past position LIMIT; return LIMIT if nothing is found before LIMIT.
767 The property values are compared with `eq'.
768 If the property is constant all the way to the end of OBJECT, return the
769 last valid position in OBJECT. */)
770 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
772 if (STRINGP (object))
774 position = Fnext_single_property_change (position, prop, object, limit);
775 if (NILP (position))
777 if (NILP (limit))
778 position = make_number (SCHARS (object));
779 else
781 CHECK_NUMBER (limit);
782 position = limit;
786 else
788 Lisp_Object initial_value, value;
789 ptrdiff_t count = SPECPDL_INDEX ();
791 if (! NILP (object))
792 CHECK_BUFFER (object);
794 if (BUFFERP (object) && current_buffer != XBUFFER (object))
796 record_unwind_current_buffer ();
797 Fset_buffer (object);
800 CHECK_NUMBER_COERCE_MARKER (position);
802 initial_value = Fget_char_property (position, prop, object);
804 if (NILP (limit))
805 XSETFASTINT (limit, ZV);
806 else
807 CHECK_NUMBER_COERCE_MARKER (limit);
809 if (XFASTINT (position) >= XFASTINT (limit))
811 position = limit;
812 if (XFASTINT (position) > ZV)
813 XSETFASTINT (position, ZV);
815 else
816 while (true)
818 position = Fnext_char_property_change (position, limit);
819 if (XFASTINT (position) >= XFASTINT (limit))
821 position = limit;
822 break;
825 value = Fget_char_property (position, prop, object);
826 if (!EQ (value, initial_value))
827 break;
830 unbind_to (count, Qnil);
833 return position;
836 DEFUN ("previous-single-char-property-change",
837 Fprevious_single_char_property_change,
838 Sprevious_single_char_property_change, 2, 4, 0,
839 doc: /* Return the position of previous text property or overlay change for a specific property.
840 Scans characters backward from POSITION till it finds
841 a change in the PROP property, then returns the position of the change.
842 If the optional third argument OBJECT is a buffer (or nil, which means
843 the current buffer), POSITION is a buffer position (integer or marker).
844 If OBJECT is a string, POSITION is a 0-based index into it.
846 In a string, scan runs to the start of the string, unless LIMIT is non-nil.
847 In a buffer, if LIMIT is nil or omitted, it runs to (point-min), and the
848 value cannot be less than that.
849 If the optional fourth argument LIMIT is non-nil, don't search back past
850 position LIMIT; return LIMIT if nothing is found before reaching LIMIT.
852 The property values are compared with `eq'.
853 If the property is constant all the way to the start of OBJECT, return the
854 first valid position in OBJECT. */)
855 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
857 if (STRINGP (object))
859 position = Fprevious_single_property_change (position, prop, object, limit);
860 if (NILP (position))
862 if (NILP (limit))
863 position = make_number (0);
864 else
866 CHECK_NUMBER (limit);
867 position = limit;
871 else
873 ptrdiff_t count = SPECPDL_INDEX ();
875 if (! NILP (object))
876 CHECK_BUFFER (object);
878 if (BUFFERP (object) && current_buffer != XBUFFER (object))
880 record_unwind_current_buffer ();
881 Fset_buffer (object);
884 CHECK_NUMBER_COERCE_MARKER (position);
886 if (NILP (limit))
887 XSETFASTINT (limit, BEGV);
888 else
889 CHECK_NUMBER_COERCE_MARKER (limit);
891 if (XFASTINT (position) <= XFASTINT (limit))
893 position = limit;
894 if (XFASTINT (position) < BEGV)
895 XSETFASTINT (position, BEGV);
897 else
899 Lisp_Object initial_value
900 = Fget_char_property (make_number (XFASTINT (position) - 1),
901 prop, object);
903 while (true)
905 position = Fprevious_char_property_change (position, limit);
907 if (XFASTINT (position) <= XFASTINT (limit))
909 position = limit;
910 break;
912 else
914 Lisp_Object value
915 = Fget_char_property (make_number (XFASTINT (position) - 1),
916 prop, object);
918 if (!EQ (value, initial_value))
919 break;
924 unbind_to (count, Qnil);
927 return position;
930 DEFUN ("next-property-change", Fnext_property_change,
931 Snext_property_change, 1, 3, 0,
932 doc: /* Return the position of next property change.
933 Scans characters forward from POSITION in OBJECT till it finds
934 a change in some text property, then returns the position of the change.
935 If the optional second argument OBJECT is a buffer (or nil, which means
936 the current buffer), POSITION is a buffer position (integer or marker).
937 If OBJECT is a string, POSITION is a 0-based index into it.
938 Return nil if LIMIT is nil or omitted, and the property is constant all
939 the way to the end of OBJECT; if the value is non-nil, it is a position
940 greater than POSITION, never equal.
942 If the optional third argument LIMIT is non-nil, don't search
943 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
944 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
946 register INTERVAL i, next;
948 if (NILP (object))
949 XSETBUFFER (object, current_buffer);
951 if (!NILP (limit) && !EQ (limit, Qt))
952 CHECK_NUMBER_COERCE_MARKER (limit);
954 i = validate_interval_range (object, &position, &position, soft);
956 /* If LIMIT is t, return start of next interval--don't
957 bother checking further intervals. */
958 if (EQ (limit, Qt))
960 if (!i)
961 next = i;
962 else
963 next = next_interval (i);
965 if (!next)
966 XSETFASTINT (position, (STRINGP (object)
967 ? SCHARS (object)
968 : BUF_ZV (XBUFFER (object))));
969 else
970 XSETFASTINT (position, next->position);
971 return position;
974 if (!i)
975 return limit;
977 next = next_interval (i);
979 while (next && intervals_equal (i, next)
980 && (NILP (limit) || next->position < XFASTINT (limit)))
981 next = next_interval (next);
983 if (!next
984 || (next->position
985 >= (INTEGERP (limit)
986 ? XFASTINT (limit)
987 : (STRINGP (object)
988 ? SCHARS (object)
989 : BUF_ZV (XBUFFER (object))))))
990 return limit;
991 else
992 return make_number (next->position);
995 DEFUN ("next-single-property-change", Fnext_single_property_change,
996 Snext_single_property_change, 2, 4, 0,
997 doc: /* Return the position of next property change for a specific property.
998 Scans characters forward from POSITION till it finds
999 a change in the PROP property, then returns the position of the change.
1000 If the optional third argument OBJECT is a buffer (or nil, which means
1001 the current buffer), POSITION is a buffer position (integer or marker).
1002 If OBJECT is a string, POSITION is a 0-based index into it.
1003 The property values are compared with `eq'.
1004 Return nil if LIMIT is nil or omitted, and the property is constant all
1005 the way to the end of OBJECT; if the value is non-nil, it is a position
1006 greater than POSITION, never equal.
1008 If the optional fourth argument LIMIT is non-nil, don't search
1009 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1010 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1012 register INTERVAL i, next;
1013 register Lisp_Object here_val;
1015 if (NILP (object))
1016 XSETBUFFER (object, current_buffer);
1018 if (!NILP (limit))
1019 CHECK_NUMBER_COERCE_MARKER (limit);
1021 i = validate_interval_range (object, &position, &position, soft);
1022 if (!i)
1023 return limit;
1025 here_val = textget (i->plist, prop);
1026 next = next_interval (i);
1027 while (next
1028 && EQ (here_val, textget (next->plist, prop))
1029 && (NILP (limit) || next->position < XFASTINT (limit)))
1030 next = next_interval (next);
1032 if (!next
1033 || (next->position
1034 >= (INTEGERP (limit)
1035 ? XFASTINT (limit)
1036 : (STRINGP (object)
1037 ? SCHARS (object)
1038 : BUF_ZV (XBUFFER (object))))))
1039 return limit;
1040 else
1041 return make_number (next->position);
1044 DEFUN ("previous-property-change", Fprevious_property_change,
1045 Sprevious_property_change, 1, 3, 0,
1046 doc: /* Return the position of previous property change.
1047 Scans characters backwards from POSITION in OBJECT till it finds
1048 a change in some text property, then returns the position of the change.
1049 If the optional second argument OBJECT is a buffer (or nil, which means
1050 the current buffer), POSITION is a buffer position (integer or marker).
1051 If OBJECT is a string, POSITION is a 0-based index into it.
1052 Return nil if LIMIT is nil or omitted, and the property is constant all
1053 the way to the start of OBJECT; if the value is non-nil, it is a position
1054 less than POSITION, never equal.
1056 If the optional third argument LIMIT is non-nil, don't search
1057 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1058 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1060 register INTERVAL i, previous;
1062 if (NILP (object))
1063 XSETBUFFER (object, current_buffer);
1065 if (!NILP (limit))
1066 CHECK_NUMBER_COERCE_MARKER (limit);
1068 i = validate_interval_range (object, &position, &position, soft);
1069 if (!i)
1070 return limit;
1072 /* Start with the interval containing the char before point. */
1073 if (i->position == XFASTINT (position))
1074 i = previous_interval (i);
1076 previous = previous_interval (i);
1077 while (previous && intervals_equal (previous, i)
1078 && (NILP (limit)
1079 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1080 previous = previous_interval (previous);
1082 if (!previous
1083 || (previous->position + LENGTH (previous)
1084 <= (INTEGERP (limit)
1085 ? XFASTINT (limit)
1086 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1087 return limit;
1088 else
1089 return make_number (previous->position + LENGTH (previous));
1092 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1093 Sprevious_single_property_change, 2, 4, 0,
1094 doc: /* Return the position of previous property change for a specific property.
1095 Scans characters backward from POSITION till it finds
1096 a change in the PROP property, then returns the position of the change.
1097 If the optional third argument OBJECT is a buffer (or nil, which means
1098 the current buffer), POSITION is a buffer position (integer or marker).
1099 If OBJECT is a string, POSITION is a 0-based index into it.
1100 The property values are compared with `eq'.
1101 Return nil if LIMIT is nil or omitted, and the property is constant all
1102 the way to the start of OBJECT; if the value is non-nil, it is a position
1103 less than POSITION, never equal.
1105 If the optional fourth argument LIMIT is non-nil, don't search
1106 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1107 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1109 register INTERVAL i, previous;
1110 register Lisp_Object here_val;
1112 if (NILP (object))
1113 XSETBUFFER (object, current_buffer);
1115 if (!NILP (limit))
1116 CHECK_NUMBER_COERCE_MARKER (limit);
1118 i = validate_interval_range (object, &position, &position, soft);
1120 /* Start with the interval containing the char before point. */
1121 if (i && i->position == XFASTINT (position))
1122 i = previous_interval (i);
1124 if (!i)
1125 return limit;
1127 here_val = textget (i->plist, prop);
1128 previous = previous_interval (i);
1129 while (previous
1130 && EQ (here_val, textget (previous->plist, prop))
1131 && (NILP (limit)
1132 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1133 previous = previous_interval (previous);
1135 if (!previous
1136 || (previous->position + LENGTH (previous)
1137 <= (INTEGERP (limit)
1138 ? XFASTINT (limit)
1139 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1140 return limit;
1141 else
1142 return make_number (previous->position + LENGTH (previous));
1145 /* Used by add-text-properties and add-face-text-property. */
1147 static Lisp_Object
1148 add_text_properties_1 (Lisp_Object start, Lisp_Object end,
1149 Lisp_Object properties, Lisp_Object object,
1150 enum property_set_type set_type) {
1151 INTERVAL i, unchanged;
1152 ptrdiff_t s, len;
1153 bool modified = false;
1154 bool first_time = true;
1156 properties = validate_plist (properties);
1157 if (NILP (properties))
1158 return Qnil;
1160 if (NILP (object))
1161 XSETBUFFER (object, current_buffer);
1163 retry:
1164 i = validate_interval_range (object, &start, &end, hard);
1165 if (!i)
1166 return Qnil;
1168 s = XINT (start);
1169 len = XINT (end) - s;
1171 /* If this interval already has the properties, we can skip it. */
1172 if (interval_has_all_properties (properties, i))
1174 ptrdiff_t got = LENGTH (i) - (s - i->position);
1178 if (got >= len)
1179 return Qnil;
1180 len -= got;
1181 i = next_interval (i);
1182 got = LENGTH (i);
1184 while (interval_has_all_properties (properties, i));
1186 else if (i->position != s)
1188 /* If we're not starting on an interval boundary, we have to
1189 split this interval. */
1190 unchanged = i;
1191 i = split_interval_right (unchanged, s - unchanged->position);
1192 copy_properties (unchanged, i);
1195 if (BUFFERP (object) && first_time)
1197 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1198 ptrdiff_t prev_pos = i->position;
1200 modify_text_properties (object, start, end);
1201 /* If someone called us recursively as a side effect of
1202 modify_text_properties, and changed the intervals behind our back
1203 (could happen if lock_file, called by prepare_to_modify_buffer,
1204 triggers redisplay, and that calls add-text-properties again
1205 in the same buffer), we cannot continue with I, because its
1206 data changed. So we restart the interval analysis anew. */
1207 if (TOTAL_LENGTH (i) != prev_total_length
1208 || i->position != prev_pos)
1210 first_time = false;
1211 goto retry;
1215 /* We are at the beginning of interval I, with LEN chars to scan. */
1216 for (;;)
1218 eassert (i != 0);
1220 if (LENGTH (i) >= len)
1222 if (interval_has_all_properties (properties, i))
1224 if (BUFFERP (object))
1225 signal_after_change (XINT (start), XINT (end) - XINT (start),
1226 XINT (end) - XINT (start));
1228 eassert (modified);
1229 return Qt;
1232 if (LENGTH (i) == len)
1234 add_properties (properties, i, object, set_type);
1235 if (BUFFERP (object))
1236 signal_after_change (XINT (start), XINT (end) - XINT (start),
1237 XINT (end) - XINT (start));
1238 return Qt;
1241 /* i doesn't have the properties, and goes past the change limit */
1242 unchanged = i;
1243 i = split_interval_left (unchanged, len);
1244 copy_properties (unchanged, i);
1245 add_properties (properties, i, object, set_type);
1246 if (BUFFERP (object))
1247 signal_after_change (XINT (start), XINT (end) - XINT (start),
1248 XINT (end) - XINT (start));
1249 return Qt;
1252 len -= LENGTH (i);
1253 modified |= add_properties (properties, i, object, set_type);
1254 i = next_interval (i);
1258 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1260 DEFUN ("add-text-properties", Fadd_text_properties,
1261 Sadd_text_properties, 3, 4, 0,
1262 doc: /* Add properties to the text from START to END.
1263 The third argument PROPERTIES is a property list
1264 specifying the property values to add. If the optional fourth argument
1265 OBJECT is a buffer (or nil, which means the current buffer),
1266 START and END are buffer positions (integers or markers).
1267 If OBJECT is a string, START and END are 0-based indices into it.
1268 Return t if any property value actually changed, nil otherwise. */)
1269 (Lisp_Object start, Lisp_Object end, Lisp_Object properties,
1270 Lisp_Object object)
1272 return add_text_properties_1 (start, end, properties, object,
1273 TEXT_PROPERTY_REPLACE);
1276 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1278 DEFUN ("put-text-property", Fput_text_property,
1279 Sput_text_property, 4, 5, 0,
1280 doc: /* Set one property of the text from START to END.
1281 The third and fourth arguments PROPERTY and VALUE
1282 specify the property to add.
1283 If the optional fifth argument OBJECT is a buffer (or nil, which means
1284 the current buffer), START and END are buffer positions (integers or
1285 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1286 (Lisp_Object start, Lisp_Object end, Lisp_Object property,
1287 Lisp_Object value, Lisp_Object object)
1289 AUTO_LIST2 (properties, property, value);
1290 Fadd_text_properties (start, end, properties, object);
1291 return Qnil;
1294 DEFUN ("set-text-properties", Fset_text_properties,
1295 Sset_text_properties, 3, 4, 0,
1296 doc: /* Completely replace properties of text from START to END.
1297 The third argument PROPERTIES is the new property list.
1298 If the optional fourth argument OBJECT is a buffer (or nil, which means
1299 the current buffer), START and END are buffer positions (integers or
1300 markers). If OBJECT is a string, START and END are 0-based indices into it.
1301 If PROPERTIES is nil, the effect is to remove all properties from
1302 the designated part of OBJECT. */)
1303 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1305 return set_text_properties (start, end, properties, object, Qt);
1309 DEFUN ("add-face-text-property", Fadd_face_text_property,
1310 Sadd_face_text_property, 3, 5, 0,
1311 doc: /* Add the face property to the text from START to END.
1312 FACE specifies the face to add. It should be a valid value of the
1313 `face' property (typically a face name or a plist of face attributes
1314 and values).
1316 If any text in the region already has a non-nil `face' property, those
1317 face(s) are retained. This is done by setting the `face' property to
1318 a list of faces, with FACE as the first element (by default) and the
1319 pre-existing faces as the remaining elements.
1321 If optional fourth argument APPEND is non-nil, append FACE to the end
1322 of the face list instead.
1324 If optional fifth argument OBJECT is a buffer (or nil, which means the
1325 current buffer), START and END are buffer positions (integers or
1326 markers). If OBJECT is a string, START and END are 0-based indices
1327 into it. */)
1328 (Lisp_Object start, Lisp_Object end, Lisp_Object face,
1329 Lisp_Object append, Lisp_Object object)
1331 AUTO_LIST2 (properties, Qface, face);
1332 add_text_properties_1 (start, end, properties, object,
1333 (NILP (append)
1334 ? TEXT_PROPERTY_PREPEND
1335 : TEXT_PROPERTY_APPEND));
1336 return Qnil;
1339 /* Replace properties of text from START to END with new list of
1340 properties PROPERTIES. OBJECT is the buffer or string containing
1341 the text. OBJECT nil means use the current buffer.
1342 COHERENT_CHANGE_P nil means this is being called as an internal
1343 subroutine, rather than as a change primitive with checking of
1344 read-only, invoking change hooks, etc.. Value is nil if the
1345 function _detected_ that it did not replace any properties, non-nil
1346 otherwise. */
1348 Lisp_Object
1349 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties,
1350 Lisp_Object object, Lisp_Object coherent_change_p)
1352 register INTERVAL i;
1353 Lisp_Object ostart, oend;
1355 ostart = start;
1356 oend = end;
1358 properties = validate_plist (properties);
1360 if (NILP (object))
1361 XSETBUFFER (object, current_buffer);
1363 /* If we want no properties for a whole string,
1364 get rid of its intervals. */
1365 if (NILP (properties) && STRINGP (object)
1366 && XFASTINT (start) == 0
1367 && XFASTINT (end) == SCHARS (object))
1369 if (!string_intervals (object))
1370 return Qnil;
1372 set_string_intervals (object, NULL);
1373 return Qt;
1376 i = validate_interval_range (object, &start, &end, soft);
1378 if (!i)
1380 /* If buffer has no properties, and we want none, return now. */
1381 if (NILP (properties))
1382 return Qnil;
1384 /* Restore the original START and END values
1385 because validate_interval_range increments them for strings. */
1386 start = ostart;
1387 end = oend;
1389 i = validate_interval_range (object, &start, &end, hard);
1390 /* This can return if start == end. */
1391 if (!i)
1392 return Qnil;
1395 if (BUFFERP (object) && !NILP (coherent_change_p))
1396 modify_text_properties (object, start, end);
1398 set_text_properties_1 (start, end, properties, object, i);
1400 if (BUFFERP (object) && !NILP (coherent_change_p))
1401 signal_after_change (XINT (start), XINT (end) - XINT (start),
1402 XINT (end) - XINT (start));
1403 return Qt;
1406 /* Replace properties of text from START to END with new list of
1407 properties PROPERTIES. OBJECT is the buffer or string containing
1408 the text. This does not obey any hooks.
1409 You should provide the interval that START is located in as I.
1410 START and END can be in any order. */
1412 void
1413 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, INTERVAL i)
1415 register INTERVAL prev_changed = NULL;
1416 register ptrdiff_t s, len;
1417 INTERVAL unchanged;
1419 if (XINT (start) < XINT (end))
1421 s = XINT (start);
1422 len = XINT (end) - s;
1424 else if (XINT (end) < XINT (start))
1426 s = XINT (end);
1427 len = XINT (start) - s;
1429 else
1430 return;
1432 eassert (i);
1434 if (i->position != s)
1436 unchanged = i;
1437 i = split_interval_right (unchanged, s - unchanged->position);
1439 if (LENGTH (i) > len)
1441 copy_properties (unchanged, i);
1442 i = split_interval_left (i, len);
1443 set_properties (properties, i, object);
1444 return;
1447 set_properties (properties, i, object);
1449 if (LENGTH (i) == len)
1450 return;
1452 prev_changed = i;
1453 len -= LENGTH (i);
1454 i = next_interval (i);
1457 /* We are starting at the beginning of an interval I. LEN is positive. */
1460 eassert (i != 0);
1462 if (LENGTH (i) >= len)
1464 if (LENGTH (i) > len)
1465 i = split_interval_left (i, len);
1467 /* We have to call set_properties even if we are going to
1468 merge the intervals, so as to make the undo records
1469 and cause redisplay to happen. */
1470 set_properties (properties, i, object);
1471 if (prev_changed)
1472 merge_interval_left (i);
1473 return;
1476 len -= LENGTH (i);
1478 /* We have to call set_properties even if we are going to
1479 merge the intervals, so as to make the undo records
1480 and cause redisplay to happen. */
1481 set_properties (properties, i, object);
1482 if (!prev_changed)
1483 prev_changed = i;
1484 else
1485 prev_changed = i = merge_interval_left (i);
1487 i = next_interval (i);
1489 while (len > 0);
1492 DEFUN ("remove-text-properties", Fremove_text_properties,
1493 Sremove_text_properties, 3, 4, 0,
1494 doc: /* Remove some properties from text from START to END.
1495 The third argument PROPERTIES is a property list
1496 whose property names specify the properties to remove.
1497 \(The values stored in PROPERTIES are ignored.)
1498 If the optional fourth argument OBJECT is a buffer (or nil, which means
1499 the current buffer), START and END are buffer positions (integers or
1500 markers). If OBJECT is a string, START and END are 0-based indices into it.
1501 Return t if any property was actually removed, nil otherwise.
1503 Use `set-text-properties' if you want to remove all text properties. */)
1504 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1506 INTERVAL i, unchanged;
1507 ptrdiff_t s, len;
1508 bool modified = false;
1509 bool first_time = true;
1511 if (NILP (object))
1512 XSETBUFFER (object, current_buffer);
1514 retry:
1515 i = validate_interval_range (object, &start, &end, soft);
1516 if (!i)
1517 return Qnil;
1519 s = XINT (start);
1520 len = XINT (end) - s;
1522 /* If there are no properties on this entire interval, return. */
1523 if (! interval_has_some_properties (properties, i))
1525 ptrdiff_t got = LENGTH (i) - (s - i->position);
1529 if (got >= len)
1530 return Qnil;
1531 len -= got;
1532 i = next_interval (i);
1533 got = LENGTH (i);
1535 while (! interval_has_some_properties (properties, i));
1537 /* Split away the beginning of this interval; what we don't
1538 want to modify. */
1539 else if (i->position != s)
1541 unchanged = i;
1542 i = split_interval_right (unchanged, s - unchanged->position);
1543 copy_properties (unchanged, i);
1546 if (BUFFERP (object) && first_time)
1548 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1549 ptrdiff_t prev_pos = i->position;
1551 modify_text_properties (object, start, end);
1552 /* If someone called us recursively as a side effect of
1553 modify_text_properties, and changed the intervals behind our back
1554 (could happen if lock_file, called by prepare_to_modify_buffer,
1555 triggers redisplay, and that calls add-text-properties again
1556 in the same buffer), we cannot continue with I, because its
1557 data changed. So we restart the interval analysis anew. */
1558 if (TOTAL_LENGTH (i) != prev_total_length
1559 || i->position != prev_pos)
1561 first_time = false;
1562 goto retry;
1566 /* We are at the beginning of an interval, with len to scan */
1567 for (;;)
1569 eassert (i != 0);
1571 if (LENGTH (i) >= len)
1573 if (! interval_has_some_properties (properties, i))
1575 eassert (modified);
1576 if (BUFFERP (object))
1577 signal_after_change (XINT (start), XINT (end) - XINT (start),
1578 XINT (end) - XINT (start));
1579 return Qt;
1582 if (LENGTH (i) == len)
1584 remove_properties (properties, Qnil, i, object);
1585 if (BUFFERP (object))
1586 signal_after_change (XINT (start), XINT (end) - XINT (start),
1587 XINT (end) - XINT (start));
1588 return Qt;
1591 /* i has the properties, and goes past the change limit */
1592 unchanged = i;
1593 i = split_interval_left (i, len);
1594 copy_properties (unchanged, i);
1595 remove_properties (properties, Qnil, i, object);
1596 if (BUFFERP (object))
1597 signal_after_change (XINT (start), XINT (end) - XINT (start),
1598 XINT (end) - XINT (start));
1599 return Qt;
1602 len -= LENGTH (i);
1603 modified |= remove_properties (properties, Qnil, i, object);
1604 i = next_interval (i);
1608 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1609 Sremove_list_of_text_properties, 3, 4, 0,
1610 doc: /* Remove some properties from text from START to END.
1611 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1612 If the optional fourth argument OBJECT is a buffer (or nil, which means
1613 the current buffer), START and END are buffer positions (integers or
1614 markers). If OBJECT is a string, START and END are 0-based indices into it.
1615 Return t if any property was actually removed, nil otherwise. */)
1616 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1618 INTERVAL i, unchanged;
1619 ptrdiff_t s, len;
1620 bool modified = false;
1621 Lisp_Object properties;
1622 properties = list_of_properties;
1624 if (NILP (object))
1625 XSETBUFFER (object, current_buffer);
1627 i = validate_interval_range (object, &start, &end, soft);
1628 if (!i)
1629 return Qnil;
1631 s = XINT (start);
1632 len = XINT (end) - s;
1634 /* If there are no properties on the interval, return. */
1635 if (! interval_has_some_properties_list (properties, i))
1637 ptrdiff_t got = LENGTH (i) - (s - i->position);
1641 if (got >= len)
1642 return Qnil;
1643 len -= got;
1644 i = next_interval (i);
1645 got = LENGTH (i);
1647 while (! interval_has_some_properties_list (properties, i));
1649 /* Split away the beginning of this interval; what we don't
1650 want to modify. */
1651 else if (i->position != s)
1653 unchanged = i;
1654 i = split_interval_right (unchanged, s - unchanged->position);
1655 copy_properties (unchanged, i);
1658 /* We are at the beginning of an interval, with len to scan.
1659 The flag MODIFIED records if changes have been made.
1660 When object is a buffer, we must call modify_text_properties
1661 before changes are made and signal_after_change when we are done.
1662 Call modify_text_properties before calling remove_properties if !MODIFIED,
1663 and call signal_after_change before returning if MODIFIED. */
1664 for (;;)
1666 eassert (i != 0);
1668 if (LENGTH (i) >= len)
1670 if (! interval_has_some_properties_list (properties, i))
1672 if (modified)
1674 if (BUFFERP (object))
1675 signal_after_change (XINT (start),
1676 XINT (end) - XINT (start),
1677 XINT (end) - XINT (start));
1678 return Qt;
1680 else
1681 return Qnil;
1683 else if (LENGTH (i) == len)
1685 if (!modified && BUFFERP (object))
1686 modify_text_properties (object, start, end);
1687 remove_properties (Qnil, properties, i, object);
1688 if (BUFFERP (object))
1689 signal_after_change (XINT (start), XINT (end) - XINT (start),
1690 XINT (end) - XINT (start));
1691 return Qt;
1693 else
1694 { /* i has the properties, and goes past the change limit. */
1695 unchanged = i;
1696 i = split_interval_left (i, len);
1697 copy_properties (unchanged, i);
1698 if (!modified && BUFFERP (object))
1699 modify_text_properties (object, start, end);
1700 remove_properties (Qnil, properties, i, object);
1701 if (BUFFERP (object))
1702 signal_after_change (XINT (start), XINT (end) - XINT (start),
1703 XINT (end) - XINT (start));
1704 return Qt;
1707 if (interval_has_some_properties_list (properties, i))
1709 if (!modified && BUFFERP (object))
1710 modify_text_properties (object, start, end);
1711 remove_properties (Qnil, properties, i, object);
1712 modified = true;
1714 len -= LENGTH (i);
1715 i = next_interval (i);
1716 if (!i)
1718 if (modified)
1720 if (BUFFERP (object))
1721 signal_after_change (XINT (start),
1722 XINT (end) - XINT (start),
1723 XINT (end) - XINT (start));
1724 return Qt;
1726 else
1727 return Qnil;
1732 DEFUN ("text-property-any", Ftext_property_any,
1733 Stext_property_any, 4, 5, 0,
1734 doc: /* Check text from START to END for property PROPERTY equaling VALUE.
1735 If so, return the position of the first character whose property PROPERTY
1736 is `eq' to VALUE. Otherwise return nil.
1737 If the optional fifth argument OBJECT is a buffer (or nil, which means
1738 the current buffer), START and END are buffer positions (integers or
1739 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1740 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1742 register INTERVAL i;
1743 register ptrdiff_t e, pos;
1745 if (NILP (object))
1746 XSETBUFFER (object, current_buffer);
1747 i = validate_interval_range (object, &start, &end, soft);
1748 if (!i)
1749 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1750 e = XINT (end);
1752 while (i)
1754 if (i->position >= e)
1755 break;
1756 if (EQ (textget (i->plist, property), value))
1758 pos = i->position;
1759 if (pos < XINT (start))
1760 pos = XINT (start);
1761 return make_number (pos);
1763 i = next_interval (i);
1765 return Qnil;
1768 DEFUN ("text-property-not-all", Ftext_property_not_all,
1769 Stext_property_not_all, 4, 5, 0,
1770 doc: /* Check text from START to END for property PROPERTY not equaling VALUE.
1771 If so, return the position of the first character whose property PROPERTY
1772 is not `eq' to VALUE. Otherwise, return nil.
1773 If the optional fifth argument OBJECT is a buffer (or nil, which means
1774 the current buffer), START and END are buffer positions (integers or
1775 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1776 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1778 register INTERVAL i;
1779 register ptrdiff_t s, e;
1781 if (NILP (object))
1782 XSETBUFFER (object, current_buffer);
1783 i = validate_interval_range (object, &start, &end, soft);
1784 if (!i)
1785 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1786 s = XINT (start);
1787 e = XINT (end);
1789 while (i)
1791 if (i->position >= e)
1792 break;
1793 if (! EQ (textget (i->plist, property), value))
1795 if (i->position > s)
1796 s = i->position;
1797 return make_number (s);
1799 i = next_interval (i);
1801 return Qnil;
1805 /* Return the direction from which the text-property PROP would be
1806 inherited by any new text inserted at POS: 1 if it would be
1807 inherited from the char after POS, -1 if it would be inherited from
1808 the char before POS, and 0 if from neither.
1809 BUFFER can be either a buffer or nil (meaning current buffer). */
1812 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1814 bool ignore_previous_character;
1815 Lisp_Object prev_pos = make_number (XINT (pos) - 1);
1816 Lisp_Object front_sticky;
1817 bool is_rear_sticky = true, is_front_sticky = false; /* defaults */
1818 Lisp_Object defalt = Fassq (prop, Vtext_property_default_nonsticky);
1820 if (NILP (buffer))
1821 XSETBUFFER (buffer, current_buffer);
1823 ignore_previous_character = XINT (pos) <= BUF_BEGV (XBUFFER (buffer));
1825 if (ignore_previous_character || (CONSP (defalt) && !NILP (XCDR (defalt))))
1826 is_rear_sticky = false;
1827 else
1829 Lisp_Object rear_non_sticky
1830 = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1832 if (!NILP (CONSP (rear_non_sticky)
1833 ? Fmemq (prop, rear_non_sticky)
1834 : rear_non_sticky))
1835 /* PROP is rear-non-sticky. */
1836 is_rear_sticky = false;
1839 /* Consider following character. */
1840 /* This signals an arg-out-of-range error if pos is outside the
1841 buffer's accessible range. */
1842 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1844 if (EQ (front_sticky, Qt)
1845 || (CONSP (front_sticky)
1846 && !NILP (Fmemq (prop, front_sticky))))
1847 /* PROP is inherited from after. */
1848 is_front_sticky = true;
1850 /* Simple cases, where the properties are consistent. */
1851 if (is_rear_sticky && !is_front_sticky)
1852 return -1;
1853 else if (!is_rear_sticky && is_front_sticky)
1854 return 1;
1855 else if (!is_rear_sticky && !is_front_sticky)
1856 return 0;
1858 /* The stickiness properties are inconsistent, so we have to
1859 disambiguate. Basically, rear-sticky wins, _except_ if the
1860 property that would be inherited has a value of nil, in which case
1861 front-sticky wins. */
1862 if (ignore_previous_character
1863 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1864 return 1;
1865 else
1866 return -1;
1870 /* Copying properties between objects. */
1872 /* Add properties from START to END of SRC, starting at POS in DEST.
1873 SRC and DEST may each refer to strings or buffers.
1874 Optional sixth argument PROP causes only that property to be copied.
1875 Properties are copied to DEST as if by `add-text-properties'.
1876 Return t if any property value actually changed, nil otherwise. */
1878 /* Note this can GC when DEST is a buffer. */
1880 Lisp_Object
1881 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src,
1882 Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1884 INTERVAL i;
1885 Lisp_Object res;
1886 Lisp_Object stuff;
1887 Lisp_Object plist;
1888 ptrdiff_t s, e, e2, p, len;
1889 bool modified = false;
1891 i = validate_interval_range (src, &start, &end, soft);
1892 if (!i)
1893 return Qnil;
1895 CHECK_NUMBER_COERCE_MARKER (pos);
1897 Lisp_Object dest_start, dest_end;
1899 e = XINT (pos) + (XINT (end) - XINT (start));
1900 if (MOST_POSITIVE_FIXNUM < e)
1901 args_out_of_range (pos, end);
1902 dest_start = pos;
1903 XSETFASTINT (dest_end, e);
1904 /* Apply this to a copy of pos; it will try to increment its arguments,
1905 which we don't want. */
1906 validate_interval_range (dest, &dest_start, &dest_end, soft);
1909 s = XINT (start);
1910 e = XINT (end);
1911 p = XINT (pos);
1913 stuff = Qnil;
1915 while (s < e)
1917 e2 = i->position + LENGTH (i);
1918 if (e2 > e)
1919 e2 = e;
1920 len = e2 - s;
1922 plist = i->plist;
1923 if (! NILP (prop))
1924 while (! NILP (plist))
1926 if (EQ (Fcar (plist), prop))
1928 plist = list2 (prop, Fcar (Fcdr (plist)));
1929 break;
1931 plist = Fcdr (Fcdr (plist));
1933 if (! NILP (plist))
1934 /* Must defer modifications to the interval tree in case
1935 src and dest refer to the same string or buffer. */
1936 stuff = Fcons (list3 (make_number (p), make_number (p + len), plist),
1937 stuff);
1939 i = next_interval (i);
1940 if (!i)
1941 break;
1943 p += len;
1944 s = i->position;
1947 while (! NILP (stuff))
1949 res = Fcar (stuff);
1950 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1951 Fcar (Fcdr (Fcdr (res))), dest);
1952 if (! NILP (res))
1953 modified = true;
1954 stuff = Fcdr (stuff);
1957 return modified ? Qt : Qnil;
1961 /* Return a list representing the text properties of OBJECT between
1962 START and END. if PROP is non-nil, report only on that property.
1963 Each result list element has the form (S E PLIST), where S and E
1964 are positions in OBJECT and PLIST is a property list containing the
1965 text properties of OBJECT between S and E. Value is nil if OBJECT
1966 doesn't contain text properties between START and END. */
1968 Lisp_Object
1969 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1971 struct interval *i;
1972 Lisp_Object result;
1974 result = Qnil;
1976 i = validate_interval_range (object, &start, &end, soft);
1977 if (i)
1979 ptrdiff_t s = XINT (start);
1980 ptrdiff_t e = XINT (end);
1982 while (s < e)
1984 ptrdiff_t interval_end, len;
1985 Lisp_Object plist;
1987 interval_end = i->position + LENGTH (i);
1988 if (interval_end > e)
1989 interval_end = e;
1990 len = interval_end - s;
1992 plist = i->plist;
1994 if (!NILP (prop))
1995 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1996 if (EQ (XCAR (plist), prop))
1998 plist = list2 (prop, Fcar (XCDR (plist)));
1999 break;
2002 if (!NILP (plist))
2003 result = Fcons (list3 (make_number (s), make_number (s + len),
2004 plist),
2005 result);
2007 i = next_interval (i);
2008 if (!i)
2009 break;
2010 s = i->position;
2014 return result;
2018 /* Add text properties to OBJECT from LIST. LIST is a list of triples
2019 (START END PLIST), where START and END are positions and PLIST is a
2020 property list containing the text properties to add. Adjust START
2021 and END positions by DELTA before adding properties. */
2023 void
2024 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
2026 for (; CONSP (list); list = XCDR (list))
2028 Lisp_Object item, start, end, plist;
2030 item = XCAR (list);
2031 start = make_number (XINT (XCAR (item)) + XINT (delta));
2032 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
2033 plist = XCAR (XCDR (XCDR (item)));
2035 Fadd_text_properties (start, end, plist, object);
2041 /* Modify end-points of ranges in LIST destructively, and return the
2042 new list. LIST is a list as returned from text_property_list.
2043 Discard properties that begin at or after NEW_END, and limit
2044 end-points to NEW_END. */
2046 Lisp_Object
2047 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
2049 Lisp_Object prev = Qnil, head = list;
2050 ptrdiff_t max = XINT (new_end);
2052 for (; CONSP (list); prev = list, list = XCDR (list))
2054 Lisp_Object item, beg, end;
2056 item = XCAR (list);
2057 beg = XCAR (item);
2058 end = 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 (XINT (end) > max)
2070 /* The end-point is past the end of the new string. */
2071 XSETCAR (XCDR (item), new_end);
2074 return head;
2079 /* Call the modification hook functions in LIST, each with START and END. */
2081 static void
2082 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2084 while (!NILP (list))
2086 call2 (Fcar (list), start, end);
2087 list = Fcdr (list);
2091 /* Check for read-only intervals between character positions START ... END,
2092 in BUF, and signal an error if we find one.
2094 Then check for any modification hooks in the range.
2095 Create a list of all these hooks in lexicographic order,
2096 eliminating consecutive extra copies of the same hook. Then call
2097 those hooks in order, with START and END - 1 as arguments. */
2099 void
2100 verify_interval_modification (struct buffer *buf,
2101 ptrdiff_t start, ptrdiff_t end)
2103 INTERVAL intervals = buffer_intervals (buf);
2104 INTERVAL i;
2105 Lisp_Object hooks;
2106 Lisp_Object prev_mod_hooks;
2107 Lisp_Object mod_hooks;
2109 hooks = Qnil;
2110 prev_mod_hooks = Qnil;
2111 mod_hooks = Qnil;
2113 interval_insert_behind_hooks = Qnil;
2114 interval_insert_in_front_hooks = Qnil;
2116 if (!intervals)
2117 return;
2119 if (start > end)
2121 ptrdiff_t temp = start;
2122 start = end;
2123 end = temp;
2126 /* For an insert operation, check the two chars around the position. */
2127 if (start == end)
2129 INTERVAL prev = NULL;
2130 Lisp_Object before, after;
2132 /* Set I to the interval containing the char after START,
2133 and PREV to the interval containing the char before START.
2134 Either one may be null. They may be equal. */
2135 i = find_interval (intervals, start);
2137 if (start == BUF_BEGV (buf))
2138 prev = 0;
2139 else if (i->position == start)
2140 prev = previous_interval (i);
2141 else if (i->position < start)
2142 prev = i;
2143 if (start == BUF_ZV (buf))
2144 i = 0;
2146 /* If Vinhibit_read_only is set and is not a list, we can
2147 skip the read_only checks. */
2148 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2150 /* If I and PREV differ we need to check for the read-only
2151 property together with its stickiness. If either I or
2152 PREV are 0, this check is all we need.
2153 We have to take special care, since read-only may be
2154 indirectly defined via the category property. */
2155 if (i != prev)
2157 if (i)
2159 after = textget (i->plist, Qread_only);
2161 /* If interval I is read-only and read-only is
2162 front-sticky, inhibit insertion.
2163 Check for read-only as well as category. */
2164 if (! NILP (after)
2165 && NILP (Fmemq (after, Vinhibit_read_only)))
2167 Lisp_Object tem;
2169 tem = textget (i->plist, Qfront_sticky);
2170 if (TMEM (Qread_only, tem)
2171 || (NILP (Fplist_get (i->plist, Qread_only))
2172 && TMEM (Qcategory, tem)))
2173 text_read_only (after);
2177 if (prev)
2179 before = textget (prev->plist, Qread_only);
2181 /* If interval PREV is read-only and read-only isn't
2182 rear-nonsticky, inhibit insertion.
2183 Check for read-only as well as category. */
2184 if (! NILP (before)
2185 && NILP (Fmemq (before, Vinhibit_read_only)))
2187 Lisp_Object tem;
2189 tem = textget (prev->plist, Qrear_nonsticky);
2190 if (! TMEM (Qread_only, tem)
2191 && (! NILP (Fplist_get (prev->plist,Qread_only))
2192 || ! TMEM (Qcategory, tem)))
2193 text_read_only (before);
2197 else if (i)
2199 after = textget (i->plist, Qread_only);
2201 /* If interval I is read-only and read-only is
2202 front-sticky, inhibit insertion.
2203 Check for read-only as well as category. */
2204 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2206 Lisp_Object tem;
2208 tem = textget (i->plist, Qfront_sticky);
2209 if (TMEM (Qread_only, tem)
2210 || (NILP (Fplist_get (i->plist, Qread_only))
2211 && TMEM (Qcategory, tem)))
2212 text_read_only (after);
2214 tem = textget (prev->plist, Qrear_nonsticky);
2215 if (! TMEM (Qread_only, tem)
2216 && (! NILP (Fplist_get (prev->plist, Qread_only))
2217 || ! TMEM (Qcategory, tem)))
2218 text_read_only (after);
2223 /* Run both insert hooks (just once if they're the same). */
2224 if (prev)
2225 interval_insert_behind_hooks
2226 = textget (prev->plist, Qinsert_behind_hooks);
2227 if (i)
2228 interval_insert_in_front_hooks
2229 = textget (i->plist, Qinsert_in_front_hooks);
2231 else
2233 /* Loop over intervals on or next to START...END,
2234 collecting their hooks. */
2236 i = find_interval (intervals, start);
2239 if (! INTERVAL_WRITABLE_P (i))
2240 text_read_only (textget (i->plist, Qread_only));
2242 if (!inhibit_modification_hooks)
2244 mod_hooks = textget (i->plist, Qmodification_hooks);
2245 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2247 hooks = Fcons (mod_hooks, hooks);
2248 prev_mod_hooks = mod_hooks;
2252 if (i->position + LENGTH (i) < end
2253 && (!NILP (BVAR (current_buffer, read_only))
2254 && NILP (Vinhibit_read_only)))
2255 xsignal1 (Qbuffer_read_only, Fcurrent_buffer ());
2257 i = next_interval (i);
2259 /* Keep going thru the interval containing the char before END. */
2260 while (i && i->position < end);
2262 if (!inhibit_modification_hooks)
2264 hooks = Fnreverse (hooks);
2265 while (! EQ (hooks, Qnil))
2267 call_mod_hooks (Fcar (hooks), make_number (start),
2268 make_number (end));
2269 hooks = Fcdr (hooks);
2275 /* Run the interval hooks for an insertion on character range START ... END.
2276 verify_interval_modification chose which hooks to run;
2277 this function is called after the insertion happens
2278 so it can indicate the range of inserted text. */
2280 void
2281 report_interval_modification (Lisp_Object start, Lisp_Object end)
2283 if (! NILP (interval_insert_behind_hooks))
2284 call_mod_hooks (interval_insert_behind_hooks, start, end);
2285 if (! NILP (interval_insert_in_front_hooks)
2286 && ! EQ (interval_insert_in_front_hooks,
2287 interval_insert_behind_hooks))
2288 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2291 void
2292 syms_of_textprop (void)
2294 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties,
2295 doc: /* Property-list used as default values.
2296 The value of a property in this list is seen as the value for every
2297 character that does not have its own value for that property. */);
2298 Vdefault_text_properties = Qnil;
2300 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist,
2301 doc: /* Alist of alternative properties for properties without a value.
2302 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2303 If a piece of text has no direct value for a particular property, then
2304 this alist is consulted. If that property appears in the alist, then
2305 the first non-nil value from the associated alternative properties is
2306 returned. */);
2307 Vchar_property_alias_alist = Qnil;
2309 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks,
2310 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2311 This also inhibits the use of the `intangible' text property.
2313 This variable is obsolete since Emacs-25.1. Use `cursor-intangible-mode'
2314 or `cursor-sensor-mode' instead. */);
2315 /* FIXME: We should make-obsolete-variable, but that signals too many
2316 warnings in code which does (let ((inhibit-point-motion-hooks t)) ...)
2317 Ideally, make-obsolete-variable should let us specify that only the nil
2318 value is obsolete, but that requires too many changes in bytecomp.el,
2319 so for now we'll keep it "obsolete via the docstring". */
2320 Vinhibit_point_motion_hooks = Qt;
2322 DEFVAR_LISP ("text-property-default-nonsticky",
2323 Vtext_property_default_nonsticky,
2324 doc: /* Alist of properties vs the corresponding non-stickiness.
2325 Each element has the form (PROPERTY . NONSTICKINESS).
2327 If a character in a buffer has PROPERTY, new text inserted adjacent to
2328 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2329 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2330 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2331 /* Text properties `syntax-table'and `display' should be nonsticky
2332 by default. */
2333 Vtext_property_default_nonsticky
2334 = list2 (Fcons (Qsyntax_table, Qt), Fcons (Qdisplay, Qt));
2336 staticpro (&interval_insert_behind_hooks);
2337 staticpro (&interval_insert_in_front_hooks);
2338 interval_insert_behind_hooks = Qnil;
2339 interval_insert_in_front_hooks = Qnil;
2342 /* Common attributes one might give text. */
2344 DEFSYM (Qfont, "font");
2345 DEFSYM (Qface, "face");
2346 DEFSYM (Qread_only, "read-only");
2347 DEFSYM (Qinvisible, "invisible");
2348 DEFSYM (Qintangible, "intangible");
2349 DEFSYM (Qcategory, "category");
2350 DEFSYM (Qlocal_map, "local-map");
2351 DEFSYM (Qfront_sticky, "front-sticky");
2352 DEFSYM (Qrear_nonsticky, "rear-nonsticky");
2353 DEFSYM (Qmouse_face, "mouse-face");
2354 DEFSYM (Qminibuffer_prompt, "minibuffer-prompt");
2356 /* Properties that text might use to specify certain actions. */
2358 DEFSYM (Qpoint_left, "point-left");
2359 DEFSYM (Qpoint_entered, "point-entered");
2361 defsubr (&Stext_properties_at);
2362 defsubr (&Sget_text_property);
2363 defsubr (&Sget_char_property);
2364 defsubr (&Sget_char_property_and_overlay);
2365 defsubr (&Snext_char_property_change);
2366 defsubr (&Sprevious_char_property_change);
2367 defsubr (&Snext_single_char_property_change);
2368 defsubr (&Sprevious_single_char_property_change);
2369 defsubr (&Snext_property_change);
2370 defsubr (&Snext_single_property_change);
2371 defsubr (&Sprevious_property_change);
2372 defsubr (&Sprevious_single_property_change);
2373 defsubr (&Sadd_text_properties);
2374 defsubr (&Sput_text_property);
2375 defsubr (&Sset_text_properties);
2376 defsubr (&Sadd_face_text_property);
2377 defsubr (&Sremove_text_properties);
2378 defsubr (&Sremove_list_of_text_properties);
2379 defsubr (&Stext_property_any);
2380 defsubr (&Stext_property_not_all);