emacs-lisp/package.el (package--read-pkg-desc): Fix tar-desc reference.
[emacs.git] / src / textprop.c
blob35f22bf454e6467be43e73b4482127149d0f5b2c
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 non-zero and set o2 to
56 the o1's cdr. Otherwise, return zero. 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 (0), 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 #define soft 0
132 #define hard 1
134 INTERVAL
135 validate_interval_range (Lisp_Object object, Lisp_Object *begin,
136 Lisp_Object *end, bool force)
138 INTERVAL i;
139 ptrdiff_t searchpos;
141 CHECK_STRING_OR_BUFFER (object);
142 CHECK_NUMBER_COERCE_MARKER (*begin);
143 CHECK_NUMBER_COERCE_MARKER (*end);
145 /* If we are asked for a point, but from a subr which operates
146 on a range, then return nothing. */
147 if (EQ (*begin, *end) && begin != end)
148 return NULL;
150 if (XINT (*begin) > XINT (*end))
152 Lisp_Object n;
153 n = *begin;
154 *begin = *end;
155 *end = n;
158 if (BUFFERP (object))
160 register struct buffer *b = XBUFFER (object);
162 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
163 && XINT (*end) <= BUF_ZV (b)))
164 args_out_of_range (*begin, *end);
165 i = buffer_intervals (b);
167 /* If there's no text, there are no properties. */
168 if (BUF_BEGV (b) == BUF_ZV (b))
169 return NULL;
171 searchpos = XINT (*begin);
173 else
175 ptrdiff_t len = SCHARS (object);
177 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
178 && XINT (*end) <= len))
179 args_out_of_range (*begin, *end);
180 XSETFASTINT (*begin, XFASTINT (*begin));
181 if (begin != end)
182 XSETFASTINT (*end, XFASTINT (*end));
183 i = string_intervals (object);
185 if (len == 0)
186 return NULL;
188 searchpos = XINT (*begin);
191 if (!i)
192 return (force ? create_root_interval (object) : i);
194 return find_interval (i, searchpos);
197 /* Validate LIST as a property list. If LIST is not a list, then
198 make one consisting of (LIST nil). Otherwise, verify that LIST
199 is even numbered and thus suitable as a plist. */
201 static Lisp_Object
202 validate_plist (Lisp_Object list)
204 if (NILP (list))
205 return Qnil;
207 if (CONSP (list))
209 bool odd_length = 0;
210 Lisp_Object tail;
211 for (tail = list; CONSP (tail); tail = XCDR (tail))
213 odd_length ^= 1;
214 QUIT;
216 if (odd_length)
217 error ("Odd length text property list");
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 = 0;
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 zero. */
244 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
245 return 0;
247 /* Property has same value on both lists; go to next one. */
248 found = 1;
249 break;
252 if (! found)
253 return 0;
256 return 1;
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 1;
278 return 0;
281 /* Return nonzero 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 1;
300 return 0;
303 /* Changing the plists of individual intervals. */
305 /* Return the value of PROP in property-list PLIST, or Qunbound if it
306 has none. */
307 static Lisp_Object
308 property_value (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 = 0;
377 struct gcpro gcpro1, gcpro2, gcpro3;
379 tail1 = plist;
380 sym1 = Qnil;
381 val1 = Qnil;
382 /* No need to protect OBJECT, because we can GC only in the case
383 where it is a buffer, and live buffers are always protected.
384 I and its plist are also protected, via OBJECT. */
385 GCPRO3 (tail1, sym1, val1);
387 /* Go through each element of PLIST. */
388 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
390 bool found = 0;
391 sym1 = XCAR (tail1);
392 val1 = Fcar (XCDR (tail1));
394 /* Go through I's plist, looking for sym1 */
395 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
396 if (EQ (sym1, XCAR (tail2)))
398 /* No need to gcpro, because tail2 protects this
399 and it must be a cons cell (we get an error otherwise). */
400 register Lisp_Object this_cdr;
402 this_cdr = XCDR (tail2);
403 /* Found the property. Now check its value. */
404 found = 1;
406 /* The properties have the same value on both lists.
407 Continue to the next property. */
408 if (EQ (val1, Fcar (this_cdr)))
409 break;
411 /* Record this change in the buffer, for undo purposes. */
412 if (BUFFERP (object))
414 record_property_change (i->position, LENGTH (i),
415 sym1, Fcar (this_cdr), object);
418 /* I's property has a different value -- change it */
419 if (set_type == TEXT_PROPERTY_REPLACE)
420 Fsetcar (this_cdr, val1);
421 else {
422 if (CONSP (Fcar (this_cdr)) &&
423 /* Special-case anonymous face properties. */
424 (! EQ (sym1, Qface) ||
425 NILP (Fkeywordp (Fcar (Fcar (this_cdr))))))
426 /* The previous value is a list, so prepend (or
427 append) the new value to this list. */
428 if (set_type == TEXT_PROPERTY_PREPEND)
429 Fsetcar (this_cdr, Fcons (val1, Fcar (this_cdr)));
430 else
431 nconc2 (Fcar (this_cdr), list1 (val1));
432 else {
433 /* The previous value is a single value, so make it
434 into a list. */
435 if (set_type == TEXT_PROPERTY_PREPEND)
436 Fsetcar (this_cdr, list2 (val1, Fcar (this_cdr)));
437 else
438 Fsetcar (this_cdr, list2 (Fcar (this_cdr), val1));
441 changed = 1;
442 break;
445 if (! found)
447 /* Record this change in the buffer, for undo purposes. */
448 if (BUFFERP (object))
450 record_property_change (i->position, LENGTH (i),
451 sym1, Qnil, object);
453 set_interval_plist (i, Fcons (sym1, Fcons (val1, i->plist)));
454 changed = 1;
458 UNGCPRO;
460 return changed;
463 /* For any members of PLIST, or LIST,
464 which are properties of I, remove them from I's plist.
465 (If PLIST is non-nil, use that, otherwise use LIST.)
466 OBJECT is the string or buffer containing I. */
468 static bool
469 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
471 Lisp_Object tail1, tail2, sym, current_plist;
472 bool changed = 0;
474 /* True means tail1 is a plist, otherwise it is a list. */
475 bool use_plist;
477 current_plist = i->plist;
479 if (! NILP (plist))
480 tail1 = plist, use_plist = 1;
481 else
482 tail1 = list, use_plist = 0;
484 /* Go through each element of LIST or PLIST. */
485 while (CONSP (tail1))
487 sym = XCAR (tail1);
489 /* First, remove the symbol if it's at the head of the list */
490 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
492 if (BUFFERP (object))
493 record_property_change (i->position, LENGTH (i),
494 sym, XCAR (XCDR (current_plist)),
495 object);
497 current_plist = XCDR (XCDR (current_plist));
498 changed = 1;
501 /* Go through I's plist, looking for SYM. */
502 tail2 = current_plist;
503 while (! NILP (tail2))
505 register Lisp_Object this;
506 this = XCDR (XCDR (tail2));
507 if (CONSP (this) && EQ (sym, XCAR (this)))
509 if (BUFFERP (object))
510 record_property_change (i->position, LENGTH (i),
511 sym, XCAR (XCDR (this)), object);
513 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
514 changed = 1;
516 tail2 = this;
519 /* Advance thru TAIL1 one way or the other. */
520 tail1 = XCDR (tail1);
521 if (use_plist && CONSP (tail1))
522 tail1 = XCDR (tail1);
525 if (changed)
526 set_interval_plist (i, current_plist);
527 return changed;
530 /* Returns the interval of POSITION in OBJECT.
531 POSITION is BEG-based. */
533 INTERVAL
534 interval_of (ptrdiff_t position, Lisp_Object object)
536 register INTERVAL i;
537 ptrdiff_t beg, end;
539 if (NILP (object))
540 XSETBUFFER (object, current_buffer);
541 else if (EQ (object, Qt))
542 return NULL;
544 CHECK_STRING_OR_BUFFER (object);
546 if (BUFFERP (object))
548 register struct buffer *b = XBUFFER (object);
550 beg = BUF_BEGV (b);
551 end = BUF_ZV (b);
552 i = buffer_intervals (b);
554 else
556 beg = 0;
557 end = SCHARS (object);
558 i = string_intervals (object);
561 if (!(beg <= position && position <= end))
562 args_out_of_range (make_number (position), make_number (position));
563 if (beg == end || !i)
564 return NULL;
566 return find_interval (i, position);
569 DEFUN ("text-properties-at", Ftext_properties_at,
570 Stext_properties_at, 1, 2, 0,
571 doc: /* Return the list of properties of the character at POSITION in OBJECT.
572 If the optional second argument OBJECT is a buffer (or nil, which means
573 the current buffer), POSITION is a buffer position (integer or marker).
574 If OBJECT is a string, POSITION is a 0-based index into it.
575 If POSITION is at the end of OBJECT, the value is nil. */)
576 (Lisp_Object position, Lisp_Object object)
578 register INTERVAL i;
580 if (NILP (object))
581 XSETBUFFER (object, current_buffer);
583 i = validate_interval_range (object, &position, &position, soft);
584 if (!i)
585 return Qnil;
586 /* If POSITION is at the end of the interval,
587 it means it's the end of OBJECT.
588 There are no properties at the very end,
589 since no character follows. */
590 if (XINT (position) == LENGTH (i) + i->position)
591 return Qnil;
593 return i->plist;
596 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
597 doc: /* Return the value of POSITION's property PROP, in OBJECT.
598 OBJECT should be a buffer or a string; if omitted or nil, it defaults
599 to the current buffer.
600 If POSITION is at the end of OBJECT, the value is nil. */)
601 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
603 return textget (Ftext_properties_at (position, object), prop);
606 /* Return the value of char's property PROP, in OBJECT at POSITION.
607 OBJECT is optional and defaults to the current buffer.
608 If OVERLAY is non-0, then in the case that the returned property is from
609 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
610 returned in *OVERLAY.
611 If POSITION is at the end of OBJECT, the value is nil.
612 If OBJECT is a buffer, then overlay properties are considered as well as
613 text properties.
614 If OBJECT is a window, then that window's buffer is used, but
615 window-specific overlays are considered only if they are associated
616 with OBJECT. */
617 Lisp_Object
618 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
620 struct window *w = 0;
622 CHECK_NUMBER_COERCE_MARKER (position);
624 if (NILP (object))
625 XSETBUFFER (object, current_buffer);
627 if (WINDOWP (object))
629 CHECK_LIVE_WINDOW (object);
630 w = XWINDOW (object);
631 object = w->contents;
633 if (BUFFERP (object))
635 ptrdiff_t noverlays;
636 Lisp_Object *overlay_vec;
637 struct buffer *obuf = current_buffer;
639 if (XINT (position) < BUF_BEGV (XBUFFER (object))
640 || XINT (position) > BUF_ZV (XBUFFER (object)))
641 xsignal1 (Qargs_out_of_range, position);
643 set_buffer_temp (XBUFFER (object));
645 USE_SAFE_ALLOCA;
646 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
647 noverlays = sort_overlays (overlay_vec, noverlays, w);
649 set_buffer_temp (obuf);
651 /* Now check the overlays in order of decreasing priority. */
652 while (--noverlays >= 0)
654 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
655 if (!NILP (tem))
657 if (overlay)
658 /* Return the overlay we got the property from. */
659 *overlay = overlay_vec[noverlays];
660 SAFE_FREE ();
661 return tem;
664 SAFE_FREE ();
667 if (overlay)
668 /* Indicate that the return value is not from an overlay. */
669 *overlay = Qnil;
671 /* Not a buffer, or no appropriate overlay, so fall through to the
672 simpler case. */
673 return Fget_text_property (position, prop, object);
676 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
677 doc: /* Return the value of POSITION's property PROP, in OBJECT.
678 Both overlay properties and text properties are checked.
679 OBJECT is optional and defaults to the current buffer.
680 If POSITION is at the end of OBJECT, the value is nil.
681 If OBJECT is a buffer, then overlay properties are considered as well as
682 text properties.
683 If OBJECT is a window, then that window's buffer is used, but window-specific
684 overlays are considered only if they are associated with OBJECT. */)
685 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
687 return get_char_property_and_overlay (position, prop, object, 0);
690 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
691 Sget_char_property_and_overlay, 2, 3, 0,
692 doc: /* Like `get-char-property', but with extra overlay information.
693 The value is a cons cell. Its car is the return value of `get-char-property'
694 with the same arguments--that is, the value of POSITION's property
695 PROP in OBJECT. Its cdr is the overlay in which the property was
696 found, or nil, if it was found as a text property or not found at all.
698 OBJECT is optional and defaults to the current buffer. OBJECT may be
699 a string, a buffer or a window. For strings, the cdr of the return
700 value is always nil, since strings do not have overlays. If OBJECT is
701 a window, then that window's buffer is used, but window-specific
702 overlays are considered only if they are associated with OBJECT. If
703 POSITION is at the end of OBJECT, both car and cdr are nil. */)
704 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
706 Lisp_Object overlay;
707 Lisp_Object val
708 = get_char_property_and_overlay (position, prop, object, &overlay);
709 return Fcons (val, overlay);
713 DEFUN ("next-char-property-change", Fnext_char_property_change,
714 Snext_char_property_change, 1, 2, 0,
715 doc: /* Return the position of next text property or overlay change.
716 This scans characters forward in the current buffer from POSITION till
717 it finds a change in some text property, or the beginning or end of an
718 overlay, and returns the position of that.
719 If none is found up to (point-max), the function returns (point-max).
721 If the optional second argument LIMIT is non-nil, don't search
722 past position LIMIT; return LIMIT if nothing is found before LIMIT.
723 LIMIT is a no-op if it is greater than (point-max). */)
724 (Lisp_Object position, Lisp_Object limit)
726 Lisp_Object temp;
728 temp = Fnext_overlay_change (position);
729 if (! NILP (limit))
731 CHECK_NUMBER_COERCE_MARKER (limit);
732 if (XINT (limit) < XINT (temp))
733 temp = limit;
735 return Fnext_property_change (position, Qnil, temp);
738 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
739 Sprevious_char_property_change, 1, 2, 0,
740 doc: /* Return the position of previous text property or overlay change.
741 Scans characters backward in the current buffer from POSITION till it
742 finds a change in some text property, or the beginning or end of an
743 overlay, and returns the position of that.
744 If none is found since (point-min), the function returns (point-min).
746 If the optional second argument LIMIT is non-nil, don't search
747 past position LIMIT; return LIMIT if nothing is found before LIMIT.
748 LIMIT is a no-op if it is less than (point-min). */)
749 (Lisp_Object position, Lisp_Object limit)
751 Lisp_Object temp;
753 temp = Fprevious_overlay_change (position);
754 if (! NILP (limit))
756 CHECK_NUMBER_COERCE_MARKER (limit);
757 if (XINT (limit) > XINT (temp))
758 temp = limit;
760 return Fprevious_property_change (position, Qnil, temp);
764 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
765 Snext_single_char_property_change, 2, 4, 0,
766 doc: /* Return the position of next text property or overlay change for a specific property.
767 Scans characters forward from POSITION till it finds
768 a change in the PROP property, then returns the position of the change.
769 If the optional third argument OBJECT is a buffer (or nil, which means
770 the current buffer), POSITION is a buffer position (integer or marker).
771 If OBJECT is a string, POSITION is a 0-based index into it.
773 In a string, scan runs to the end of the string.
774 In a buffer, it runs to (point-max), and the value cannot exceed that.
776 The property values are compared with `eq'.
777 If the property is constant all the way to the end of OBJECT, return the
778 last valid position in OBJECT.
779 If the optional fourth argument LIMIT is non-nil, don't search
780 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
781 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
783 if (STRINGP (object))
785 position = Fnext_single_property_change (position, prop, object, limit);
786 if (NILP (position))
788 if (NILP (limit))
789 position = make_number (SCHARS (object));
790 else
792 CHECK_NUMBER (limit);
793 position = limit;
797 else
799 Lisp_Object initial_value, value;
800 ptrdiff_t count = SPECPDL_INDEX ();
802 if (! NILP (object))
803 CHECK_BUFFER (object);
805 if (BUFFERP (object) && current_buffer != XBUFFER (object))
807 record_unwind_current_buffer ();
808 Fset_buffer (object);
811 CHECK_NUMBER_COERCE_MARKER (position);
813 initial_value = Fget_char_property (position, prop, object);
815 if (NILP (limit))
816 XSETFASTINT (limit, ZV);
817 else
818 CHECK_NUMBER_COERCE_MARKER (limit);
820 if (XFASTINT (position) >= XFASTINT (limit))
822 position = limit;
823 if (XFASTINT (position) > ZV)
824 XSETFASTINT (position, ZV);
826 else
827 while (1)
829 position = Fnext_char_property_change (position, limit);
830 if (XFASTINT (position) >= XFASTINT (limit))
832 position = limit;
833 break;
836 value = Fget_char_property (position, prop, object);
837 if (!EQ (value, initial_value))
838 break;
841 unbind_to (count, Qnil);
844 return position;
847 DEFUN ("previous-single-char-property-change",
848 Fprevious_single_char_property_change,
849 Sprevious_single_char_property_change, 2, 4, 0,
850 doc: /* Return the position of previous text property or overlay change for a specific property.
851 Scans characters backward from POSITION till it finds
852 a change in the PROP property, then returns the position of the change.
853 If the optional third argument OBJECT is a buffer (or nil, which means
854 the current buffer), POSITION is a buffer position (integer or marker).
855 If OBJECT is a string, POSITION is a 0-based index into it.
857 In a string, scan runs to the start of the string.
858 In a buffer, it runs to (point-min), and the value cannot be less than that.
860 The property values are compared with `eq'.
861 If the property is constant all the way to the start of OBJECT, return the
862 first valid position in OBJECT.
863 If the optional fourth argument LIMIT is non-nil, don't search back past
864 position LIMIT; return LIMIT if nothing is found before reaching LIMIT. */)
865 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
867 if (STRINGP (object))
869 position = Fprevious_single_property_change (position, prop, object, limit);
870 if (NILP (position))
872 if (NILP (limit))
873 position = make_number (0);
874 else
876 CHECK_NUMBER (limit);
877 position = limit;
881 else
883 ptrdiff_t count = SPECPDL_INDEX ();
885 if (! NILP (object))
886 CHECK_BUFFER (object);
888 if (BUFFERP (object) && current_buffer != XBUFFER (object))
890 record_unwind_current_buffer ();
891 Fset_buffer (object);
894 CHECK_NUMBER_COERCE_MARKER (position);
896 if (NILP (limit))
897 XSETFASTINT (limit, BEGV);
898 else
899 CHECK_NUMBER_COERCE_MARKER (limit);
901 if (XFASTINT (position) <= XFASTINT (limit))
903 position = limit;
904 if (XFASTINT (position) < BEGV)
905 XSETFASTINT (position, BEGV);
907 else
909 Lisp_Object initial_value
910 = Fget_char_property (make_number (XFASTINT (position) - 1),
911 prop, object);
913 while (1)
915 position = Fprevious_char_property_change (position, limit);
917 if (XFASTINT (position) <= XFASTINT (limit))
919 position = limit;
920 break;
922 else
924 Lisp_Object value
925 = Fget_char_property (make_number (XFASTINT (position) - 1),
926 prop, object);
928 if (!EQ (value, initial_value))
929 break;
934 unbind_to (count, Qnil);
937 return position;
940 DEFUN ("next-property-change", Fnext_property_change,
941 Snext_property_change, 1, 3, 0,
942 doc: /* Return the position of next property change.
943 Scans characters forward from POSITION in OBJECT till it finds
944 a change in some text property, then returns the position of the change.
945 If the optional second argument OBJECT is a buffer (or nil, which means
946 the current buffer), POSITION is a buffer position (integer or marker).
947 If OBJECT is a string, POSITION is a 0-based index into it.
948 Return nil if the property is constant all the way to the end of OBJECT.
949 If the value is non-nil, it is a position greater than POSITION, never equal.
951 If the optional third argument LIMIT is non-nil, don't search
952 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
953 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
955 register INTERVAL i, next;
957 if (NILP (object))
958 XSETBUFFER (object, current_buffer);
960 if (!NILP (limit) && !EQ (limit, Qt))
961 CHECK_NUMBER_COERCE_MARKER (limit);
963 i = validate_interval_range (object, &position, &position, soft);
965 /* If LIMIT is t, return start of next interval--don't
966 bother checking further intervals. */
967 if (EQ (limit, Qt))
969 if (!i)
970 next = i;
971 else
972 next = next_interval (i);
974 if (!next)
975 XSETFASTINT (position, (STRINGP (object)
976 ? SCHARS (object)
977 : BUF_ZV (XBUFFER (object))));
978 else
979 XSETFASTINT (position, next->position);
980 return position;
983 if (!i)
984 return limit;
986 next = next_interval (i);
988 while (next && intervals_equal (i, next)
989 && (NILP (limit) || next->position < XFASTINT (limit)))
990 next = next_interval (next);
992 if (!next
993 || (next->position
994 >= (INTEGERP (limit)
995 ? XFASTINT (limit)
996 : (STRINGP (object)
997 ? SCHARS (object)
998 : BUF_ZV (XBUFFER (object))))))
999 return limit;
1000 else
1001 return make_number (next->position);
1004 DEFUN ("next-single-property-change", Fnext_single_property_change,
1005 Snext_single_property_change, 2, 4, 0,
1006 doc: /* Return the position of next property change for a specific property.
1007 Scans characters forward from POSITION till it finds
1008 a change in the PROP property, then returns the position of the change.
1009 If the optional third argument OBJECT is a buffer (or nil, which means
1010 the current buffer), POSITION is a buffer position (integer or marker).
1011 If OBJECT is a string, POSITION is a 0-based index into it.
1012 The property values are compared with `eq'.
1013 Return nil if the property is constant all the way to the end of OBJECT.
1014 If the value is non-nil, it is a position greater than POSITION, never equal.
1016 If the optional fourth argument LIMIT is non-nil, don't search
1017 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1018 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1020 register INTERVAL i, next;
1021 register Lisp_Object here_val;
1023 if (NILP (object))
1024 XSETBUFFER (object, current_buffer);
1026 if (!NILP (limit))
1027 CHECK_NUMBER_COERCE_MARKER (limit);
1029 i = validate_interval_range (object, &position, &position, soft);
1030 if (!i)
1031 return limit;
1033 here_val = textget (i->plist, prop);
1034 next = next_interval (i);
1035 while (next
1036 && EQ (here_val, textget (next->plist, prop))
1037 && (NILP (limit) || next->position < XFASTINT (limit)))
1038 next = next_interval (next);
1040 if (!next
1041 || (next->position
1042 >= (INTEGERP (limit)
1043 ? XFASTINT (limit)
1044 : (STRINGP (object)
1045 ? SCHARS (object)
1046 : BUF_ZV (XBUFFER (object))))))
1047 return limit;
1048 else
1049 return make_number (next->position);
1052 DEFUN ("previous-property-change", Fprevious_property_change,
1053 Sprevious_property_change, 1, 3, 0,
1054 doc: /* Return the position of previous property change.
1055 Scans characters backwards from POSITION in OBJECT till it finds
1056 a change in some text property, then returns the position of the change.
1057 If the optional second argument OBJECT is a buffer (or nil, which means
1058 the current buffer), POSITION is a buffer position (integer or marker).
1059 If OBJECT is a string, POSITION is a 0-based index into it.
1060 Return nil if the property is constant all the way to the start of OBJECT.
1061 If the value is non-nil, it is a position less than POSITION, never equal.
1063 If the optional third argument LIMIT is non-nil, don't search
1064 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1065 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1067 register INTERVAL i, previous;
1069 if (NILP (object))
1070 XSETBUFFER (object, current_buffer);
1072 if (!NILP (limit))
1073 CHECK_NUMBER_COERCE_MARKER (limit);
1075 i = validate_interval_range (object, &position, &position, soft);
1076 if (!i)
1077 return limit;
1079 /* Start with the interval containing the char before point. */
1080 if (i->position == XFASTINT (position))
1081 i = previous_interval (i);
1083 previous = previous_interval (i);
1084 while (previous && intervals_equal (previous, i)
1085 && (NILP (limit)
1086 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1087 previous = previous_interval (previous);
1089 if (!previous
1090 || (previous->position + LENGTH (previous)
1091 <= (INTEGERP (limit)
1092 ? XFASTINT (limit)
1093 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1094 return limit;
1095 else
1096 return make_number (previous->position + LENGTH (previous));
1099 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1100 Sprevious_single_property_change, 2, 4, 0,
1101 doc: /* Return the position of previous property change for a specific property.
1102 Scans characters backward from POSITION till it finds
1103 a change in the PROP property, then returns the position of the change.
1104 If the optional third argument OBJECT is a buffer (or nil, which means
1105 the current buffer), POSITION is a buffer position (integer or marker).
1106 If OBJECT is a string, POSITION is a 0-based index into it.
1107 The property values are compared with `eq'.
1108 Return nil if the property is constant all the way to the start of OBJECT.
1109 If the value is non-nil, it is a position less than POSITION, never equal.
1111 If the optional fourth argument LIMIT is non-nil, don't search
1112 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1113 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1115 register INTERVAL i, previous;
1116 register Lisp_Object here_val;
1118 if (NILP (object))
1119 XSETBUFFER (object, current_buffer);
1121 if (!NILP (limit))
1122 CHECK_NUMBER_COERCE_MARKER (limit);
1124 i = validate_interval_range (object, &position, &position, soft);
1126 /* Start with the interval containing the char before point. */
1127 if (i && i->position == XFASTINT (position))
1128 i = previous_interval (i);
1130 if (!i)
1131 return limit;
1133 here_val = textget (i->plist, prop);
1134 previous = previous_interval (i);
1135 while (previous
1136 && EQ (here_val, textget (previous->plist, prop))
1137 && (NILP (limit)
1138 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1139 previous = previous_interval (previous);
1141 if (!previous
1142 || (previous->position + LENGTH (previous)
1143 <= (INTEGERP (limit)
1144 ? XFASTINT (limit)
1145 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1146 return limit;
1147 else
1148 return make_number (previous->position + LENGTH (previous));
1151 /* Used by add-text-properties and add-face-text-property. */
1153 static Lisp_Object
1154 add_text_properties_1 (Lisp_Object start, Lisp_Object end,
1155 Lisp_Object properties, Lisp_Object object,
1156 enum property_set_type set_type) {
1157 INTERVAL i, unchanged;
1158 ptrdiff_t s, len;
1159 bool modified = 0;
1160 struct gcpro gcpro1;
1161 bool first_time = 1;
1163 properties = validate_plist (properties);
1164 if (NILP (properties))
1165 return Qnil;
1167 if (NILP (object))
1168 XSETBUFFER (object, current_buffer);
1170 retry:
1171 i = validate_interval_range (object, &start, &end, hard);
1172 if (!i)
1173 return Qnil;
1175 s = XINT (start);
1176 len = XINT (end) - s;
1178 /* No need to protect OBJECT, because we GC only if it's a buffer,
1179 and live buffers are always protected. */
1180 GCPRO1 (properties);
1182 /* If this interval already has the properties, we can skip it. */
1183 if (interval_has_all_properties (properties, i))
1185 ptrdiff_t got = LENGTH (i) - (s - i->position);
1189 if (got >= len)
1190 RETURN_UNGCPRO (Qnil);
1191 len -= got;
1192 i = next_interval (i);
1193 got = LENGTH (i);
1195 while (interval_has_all_properties (properties, i));
1197 else if (i->position != s)
1199 /* If we're not starting on an interval boundary, we have to
1200 split this interval. */
1201 unchanged = i;
1202 i = split_interval_right (unchanged, s - unchanged->position);
1203 copy_properties (unchanged, i);
1206 if (BUFFERP (object) && first_time)
1208 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1209 ptrdiff_t prev_pos = i->position;
1211 modify_text_properties (object, start, end);
1212 /* If someone called us recursively as a side effect of
1213 modify_text_properties, and changed the intervals behind our back
1214 (could happen if lock_file, called by prepare_to_modify_buffer,
1215 triggers redisplay, and that calls add-text-properties again
1216 in the same buffer), we cannot continue with I, because its
1217 data changed. So we restart the interval analysis anew. */
1218 if (TOTAL_LENGTH (i) != prev_total_length
1219 || i->position != prev_pos)
1221 first_time = 0;
1222 goto retry;
1226 /* We are at the beginning of interval I, with LEN chars to scan. */
1227 for (;;)
1229 eassert (i != 0);
1231 if (LENGTH (i) >= len)
1233 /* We can UNGCPRO safely here, because there will be just
1234 one more chance to gc, in the next call to add_properties,
1235 and after that we will not need PROPERTIES or OBJECT again. */
1236 UNGCPRO;
1238 if (interval_has_all_properties (properties, i))
1240 if (BUFFERP (object))
1241 signal_after_change (XINT (start), XINT (end) - XINT (start),
1242 XINT (end) - XINT (start));
1244 eassert (modified);
1245 return Qt;
1248 if (LENGTH (i) == len)
1250 add_properties (properties, i, object, set_type);
1251 if (BUFFERP (object))
1252 signal_after_change (XINT (start), XINT (end) - XINT (start),
1253 XINT (end) - XINT (start));
1254 return Qt;
1257 /* i doesn't have the properties, and goes past the change limit */
1258 unchanged = i;
1259 i = split_interval_left (unchanged, len);
1260 copy_properties (unchanged, i);
1261 add_properties (properties, i, object, set_type);
1262 if (BUFFERP (object))
1263 signal_after_change (XINT (start), XINT (end) - XINT (start),
1264 XINT (end) - XINT (start));
1265 return Qt;
1268 len -= LENGTH (i);
1269 modified |= add_properties (properties, i, object, set_type);
1270 i = next_interval (i);
1274 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1276 DEFUN ("add-text-properties", Fadd_text_properties,
1277 Sadd_text_properties, 3, 4, 0,
1278 doc: /* Add properties to the text from START to END.
1279 The third argument PROPERTIES is a property list
1280 specifying the property values to add. If the optional fourth argument
1281 OBJECT is a buffer (or nil, which means the current buffer),
1282 START and END are buffer positions (integers or markers).
1283 If OBJECT is a string, START and END are 0-based indices into it.
1284 Return t if any property value actually changed, nil otherwise. */)
1285 (Lisp_Object start, Lisp_Object end, Lisp_Object properties,
1286 Lisp_Object object)
1288 return add_text_properties_1 (start, end, properties, object,
1289 TEXT_PROPERTY_REPLACE);
1292 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1294 DEFUN ("put-text-property", Fput_text_property,
1295 Sput_text_property, 4, 5, 0,
1296 doc: /* Set one property of the text from START to END.
1297 The third and fourth arguments PROPERTY and VALUE
1298 specify the property to add.
1299 If the optional fifth argument OBJECT is a buffer (or nil, which means
1300 the current buffer), START and END are buffer positions (integers or
1301 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1302 (Lisp_Object start, Lisp_Object end, Lisp_Object property,
1303 Lisp_Object value, Lisp_Object object)
1305 AUTO_LIST2 (properties, property, value);
1306 Fadd_text_properties (start, end, properties, object);
1307 return Qnil;
1310 DEFUN ("set-text-properties", Fset_text_properties,
1311 Sset_text_properties, 3, 4, 0,
1312 doc: /* Completely replace properties of text from START to END.
1313 The third argument PROPERTIES is the new property list.
1314 If the optional fourth argument OBJECT is a buffer (or nil, which means
1315 the current buffer), START and END are buffer positions (integers or
1316 markers). If OBJECT is a string, START and END are 0-based indices into it.
1317 If PROPERTIES is nil, the effect is to remove all properties from
1318 the designated part of OBJECT. */)
1319 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1321 return set_text_properties (start, end, properties, object, Qt);
1325 DEFUN ("add-face-text-property", Fadd_face_text_property,
1326 Sadd_face_text_property, 3, 5, 0,
1327 doc: /* Add the face property to the text from START to END.
1328 FACE specifies the face to add. It should be a valid value of the
1329 `face' property (typically a face name or a plist of face attributes
1330 and values).
1332 If any text in the region already has a non-nil `face' property, those
1333 face(s) are retained. This is done by setting the `face' property to
1334 a list of faces, with FACE as the first element (by default) and the
1335 pre-existing faces as the remaining elements.
1337 If optional fourth argument APPEND is non-nil, append FACE to the end
1338 of the face list instead.
1340 If optional fifth argument OBJECT is a buffer (or nil, which means the
1341 current buffer), START and END are buffer positions (integers or
1342 markers). If OBJECT is a string, START and END are 0-based indices
1343 into it. */)
1344 (Lisp_Object start, Lisp_Object end, Lisp_Object face,
1345 Lisp_Object append, Lisp_Object object)
1347 AUTO_LIST2 (properties, Qface, face);
1348 add_text_properties_1 (start, end, properties, object,
1349 (NILP (append)
1350 ? TEXT_PROPERTY_PREPEND
1351 : TEXT_PROPERTY_APPEND));
1352 return Qnil;
1355 /* Replace properties of text from START to END with new list of
1356 properties PROPERTIES. OBJECT is the buffer or string containing
1357 the text. OBJECT nil means use the current buffer.
1358 COHERENT_CHANGE_P nil means this is being called as an internal
1359 subroutine, rather than as a change primitive with checking of
1360 read-only, invoking change hooks, etc.. Value is nil if the
1361 function _detected_ that it did not replace any properties, non-nil
1362 otherwise. */
1364 Lisp_Object
1365 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties,
1366 Lisp_Object object, Lisp_Object coherent_change_p)
1368 register INTERVAL i;
1369 Lisp_Object ostart, oend;
1371 ostart = start;
1372 oend = end;
1374 properties = validate_plist (properties);
1376 if (NILP (object))
1377 XSETBUFFER (object, current_buffer);
1379 /* If we want no properties for a whole string,
1380 get rid of its intervals. */
1381 if (NILP (properties) && STRINGP (object)
1382 && XFASTINT (start) == 0
1383 && XFASTINT (end) == SCHARS (object))
1385 if (!string_intervals (object))
1386 return Qnil;
1388 set_string_intervals (object, NULL);
1389 return Qt;
1392 i = validate_interval_range (object, &start, &end, soft);
1394 if (!i)
1396 /* If buffer has no properties, and we want none, return now. */
1397 if (NILP (properties))
1398 return Qnil;
1400 /* Restore the original START and END values
1401 because validate_interval_range increments them for strings. */
1402 start = ostart;
1403 end = oend;
1405 i = validate_interval_range (object, &start, &end, hard);
1406 /* This can return if start == end. */
1407 if (!i)
1408 return Qnil;
1411 if (BUFFERP (object) && !NILP (coherent_change_p))
1412 modify_text_properties (object, start, end);
1414 set_text_properties_1 (start, end, properties, object, i);
1416 if (BUFFERP (object) && !NILP (coherent_change_p))
1417 signal_after_change (XINT (start), XINT (end) - XINT (start),
1418 XINT (end) - XINT (start));
1419 return Qt;
1422 /* Replace properties of text from START to END with new list of
1423 properties PROPERTIES. OBJECT is the buffer or string containing
1424 the text. This does not obey any hooks.
1425 You should provide the interval that START is located in as I.
1426 START and END can be in any order. */
1428 void
1429 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, INTERVAL i)
1431 register INTERVAL prev_changed = NULL;
1432 register ptrdiff_t s, len;
1433 INTERVAL unchanged;
1435 if (XINT (start) < XINT (end))
1437 s = XINT (start);
1438 len = XINT (end) - s;
1440 else if (XINT (end) < XINT (start))
1442 s = XINT (end);
1443 len = XINT (start) - s;
1445 else
1446 return;
1448 eassert (i);
1450 if (i->position != s)
1452 unchanged = i;
1453 i = split_interval_right (unchanged, s - unchanged->position);
1455 if (LENGTH (i) > len)
1457 copy_properties (unchanged, i);
1458 i = split_interval_left (i, len);
1459 set_properties (properties, i, object);
1460 return;
1463 set_properties (properties, i, object);
1465 if (LENGTH (i) == len)
1466 return;
1468 prev_changed = i;
1469 len -= LENGTH (i);
1470 i = next_interval (i);
1473 /* We are starting at the beginning of an interval I. LEN is positive. */
1476 eassert (i != 0);
1478 if (LENGTH (i) >= len)
1480 if (LENGTH (i) > len)
1481 i = split_interval_left (i, len);
1483 /* We have to call set_properties even if we are going to
1484 merge the intervals, so as to make the undo records
1485 and cause redisplay to happen. */
1486 set_properties (properties, i, object);
1487 if (prev_changed)
1488 merge_interval_left (i);
1489 return;
1492 len -= LENGTH (i);
1494 /* We have to call set_properties even if we are going to
1495 merge the intervals, so as to make the undo records
1496 and cause redisplay to happen. */
1497 set_properties (properties, i, object);
1498 if (!prev_changed)
1499 prev_changed = i;
1500 else
1501 prev_changed = i = merge_interval_left (i);
1503 i = next_interval (i);
1505 while (len > 0);
1508 DEFUN ("remove-text-properties", Fremove_text_properties,
1509 Sremove_text_properties, 3, 4, 0,
1510 doc: /* Remove some properties from text from START to END.
1511 The third argument PROPERTIES is a property list
1512 whose property names specify the properties to remove.
1513 \(The values stored in PROPERTIES are ignored.)
1514 If the optional fourth argument OBJECT is a buffer (or nil, which means
1515 the current buffer), START and END are buffer positions (integers or
1516 markers). If OBJECT is a string, START and END are 0-based indices into it.
1517 Return t if any property was actually removed, nil otherwise.
1519 Use `set-text-properties' if you want to remove all text properties. */)
1520 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1522 INTERVAL i, unchanged;
1523 ptrdiff_t s, len;
1524 bool modified = 0;
1525 bool first_time = 1;
1527 if (NILP (object))
1528 XSETBUFFER (object, current_buffer);
1530 retry:
1531 i = validate_interval_range (object, &start, &end, soft);
1532 if (!i)
1533 return Qnil;
1535 s = XINT (start);
1536 len = XINT (end) - s;
1538 /* If there are no properties on this entire interval, return. */
1539 if (! interval_has_some_properties (properties, i))
1541 ptrdiff_t got = LENGTH (i) - (s - i->position);
1545 if (got >= len)
1546 return Qnil;
1547 len -= got;
1548 i = next_interval (i);
1549 got = LENGTH (i);
1551 while (! interval_has_some_properties (properties, i));
1553 /* Split away the beginning of this interval; what we don't
1554 want to modify. */
1555 else if (i->position != s)
1557 unchanged = i;
1558 i = split_interval_right (unchanged, s - unchanged->position);
1559 copy_properties (unchanged, i);
1562 if (BUFFERP (object) && first_time)
1564 ptrdiff_t prev_total_length = TOTAL_LENGTH (i);
1565 ptrdiff_t prev_pos = i->position;
1567 modify_text_properties (object, start, end);
1568 /* If someone called us recursively as a side effect of
1569 modify_text_properties, and changed the intervals behind our back
1570 (could happen if lock_file, called by prepare_to_modify_buffer,
1571 triggers redisplay, and that calls add-text-properties again
1572 in the same buffer), we cannot continue with I, because its
1573 data changed. So we restart the interval analysis anew. */
1574 if (TOTAL_LENGTH (i) != prev_total_length
1575 || i->position != prev_pos)
1577 first_time = 0;
1578 goto retry;
1582 /* We are at the beginning of an interval, with len to scan */
1583 for (;;)
1585 eassert (i != 0);
1587 if (LENGTH (i) >= len)
1589 if (! interval_has_some_properties (properties, i))
1591 eassert (modified);
1592 if (BUFFERP (object))
1593 signal_after_change (XINT (start), XINT (end) - XINT (start),
1594 XINT (end) - XINT (start));
1595 return Qt;
1598 if (LENGTH (i) == len)
1600 remove_properties (properties, Qnil, i, object);
1601 if (BUFFERP (object))
1602 signal_after_change (XINT (start), XINT (end) - XINT (start),
1603 XINT (end) - XINT (start));
1604 return Qt;
1607 /* i has the properties, and goes past the change limit */
1608 unchanged = i;
1609 i = split_interval_left (i, len);
1610 copy_properties (unchanged, i);
1611 remove_properties (properties, Qnil, i, object);
1612 if (BUFFERP (object))
1613 signal_after_change (XINT (start), XINT (end) - XINT (start),
1614 XINT (end) - XINT (start));
1615 return Qt;
1618 len -= LENGTH (i);
1619 modified |= remove_properties (properties, Qnil, i, object);
1620 i = next_interval (i);
1624 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1625 Sremove_list_of_text_properties, 3, 4, 0,
1626 doc: /* Remove some properties from text from START to END.
1627 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1628 If the optional fourth argument OBJECT is a buffer (or nil, which means
1629 the current buffer), START and END are buffer positions (integers or
1630 markers). If OBJECT is a string, START and END are 0-based indices into it.
1631 Return t if any property was actually removed, nil otherwise. */)
1632 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1634 INTERVAL i, unchanged;
1635 ptrdiff_t s, len;
1636 bool modified = 0;
1637 Lisp_Object properties;
1638 properties = list_of_properties;
1640 if (NILP (object))
1641 XSETBUFFER (object, current_buffer);
1643 i = validate_interval_range (object, &start, &end, soft);
1644 if (!i)
1645 return Qnil;
1647 s = XINT (start);
1648 len = XINT (end) - s;
1650 /* If there are no properties on the interval, return. */
1651 if (! interval_has_some_properties_list (properties, i))
1653 ptrdiff_t got = LENGTH (i) - (s - i->position);
1657 if (got >= len)
1658 return Qnil;
1659 len -= got;
1660 i = next_interval (i);
1661 got = LENGTH (i);
1663 while (! interval_has_some_properties_list (properties, i));
1665 /* Split away the beginning of this interval; what we don't
1666 want to modify. */
1667 else if (i->position != s)
1669 unchanged = i;
1670 i = split_interval_right (unchanged, s - unchanged->position);
1671 copy_properties (unchanged, i);
1674 /* We are at the beginning of an interval, with len to scan.
1675 The flag `modified' records if changes have been made.
1676 When object is a buffer, we must call modify_text_properties
1677 before changes are made and signal_after_change when we are done.
1678 We call modify_text_properties before calling remove_properties if modified == 0,
1679 and we call signal_after_change before returning if modified != 0. */
1680 for (;;)
1682 eassert (i != 0);
1684 if (LENGTH (i) >= len)
1686 if (! interval_has_some_properties_list (properties, i))
1688 if (modified)
1690 if (BUFFERP (object))
1691 signal_after_change (XINT (start),
1692 XINT (end) - XINT (start),
1693 XINT (end) - XINT (start));
1694 return Qt;
1696 else
1697 return Qnil;
1699 else if (LENGTH (i) == len)
1701 if (!modified && BUFFERP (object))
1702 modify_text_properties (object, start, end);
1703 remove_properties (Qnil, properties, i, object);
1704 if (BUFFERP (object))
1705 signal_after_change (XINT (start), XINT (end) - XINT (start),
1706 XINT (end) - XINT (start));
1707 return Qt;
1709 else
1710 { /* i has the properties, and goes past the change limit. */
1711 unchanged = i;
1712 i = split_interval_left (i, len);
1713 copy_properties (unchanged, i);
1714 if (!modified && BUFFERP (object))
1715 modify_text_properties (object, start, end);
1716 remove_properties (Qnil, properties, i, object);
1717 if (BUFFERP (object))
1718 signal_after_change (XINT (start), XINT (end) - XINT (start),
1719 XINT (end) - XINT (start));
1720 return Qt;
1723 if (interval_has_some_properties_list (properties, i))
1725 if (!modified && BUFFERP (object))
1726 modify_text_properties (object, start, end);
1727 remove_properties (Qnil, properties, i, object);
1728 modified = 1;
1730 len -= LENGTH (i);
1731 i = next_interval (i);
1732 if (!i)
1734 if (modified)
1736 if (BUFFERP (object))
1737 signal_after_change (XINT (start),
1738 XINT (end) - XINT (start),
1739 XINT (end) - XINT (start));
1740 return Qt;
1742 else
1743 return Qnil;
1748 DEFUN ("text-property-any", Ftext_property_any,
1749 Stext_property_any, 4, 5, 0,
1750 doc: /* Check text from START to END for property PROPERTY equaling VALUE.
1751 If so, return the position of the first character whose property PROPERTY
1752 is `eq' to VALUE. Otherwise return nil.
1753 If the optional fifth argument OBJECT is a buffer (or nil, which means
1754 the current buffer), START and END are buffer positions (integers or
1755 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1756 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1758 register INTERVAL i;
1759 register ptrdiff_t e, pos;
1761 if (NILP (object))
1762 XSETBUFFER (object, current_buffer);
1763 i = validate_interval_range (object, &start, &end, soft);
1764 if (!i)
1765 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1766 e = XINT (end);
1768 while (i)
1770 if (i->position >= e)
1771 break;
1772 if (EQ (textget (i->plist, property), value))
1774 pos = i->position;
1775 if (pos < XINT (start))
1776 pos = XINT (start);
1777 return make_number (pos);
1779 i = next_interval (i);
1781 return Qnil;
1784 DEFUN ("text-property-not-all", Ftext_property_not_all,
1785 Stext_property_not_all, 4, 5, 0,
1786 doc: /* Check text from START to END for property PROPERTY not equaling VALUE.
1787 If so, return the position of the first character whose property PROPERTY
1788 is not `eq' to VALUE. Otherwise, return nil.
1789 If the optional fifth argument OBJECT is a buffer (or nil, which means
1790 the current buffer), START and END are buffer positions (integers or
1791 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1792 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1794 register INTERVAL i;
1795 register ptrdiff_t s, e;
1797 if (NILP (object))
1798 XSETBUFFER (object, current_buffer);
1799 i = validate_interval_range (object, &start, &end, soft);
1800 if (!i)
1801 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1802 s = XINT (start);
1803 e = XINT (end);
1805 while (i)
1807 if (i->position >= e)
1808 break;
1809 if (! EQ (textget (i->plist, property), value))
1811 if (i->position > s)
1812 s = i->position;
1813 return make_number (s);
1815 i = next_interval (i);
1817 return Qnil;
1821 /* Return the direction from which the text-property PROP would be
1822 inherited by any new text inserted at POS: 1 if it would be
1823 inherited from the char after POS, -1 if it would be inherited from
1824 the char before POS, and 0 if from neither.
1825 BUFFER can be either a buffer or nil (meaning current buffer). */
1828 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1830 bool ignore_previous_character;
1831 Lisp_Object prev_pos = make_number (XINT (pos) - 1);
1832 Lisp_Object front_sticky;
1833 bool is_rear_sticky = true, is_front_sticky = false; /* defaults */
1834 Lisp_Object defalt = Fassq (prop, Vtext_property_default_nonsticky);
1836 if (NILP (buffer))
1837 XSETBUFFER (buffer, current_buffer);
1839 ignore_previous_character = XINT (pos) <= BUF_BEGV (XBUFFER (buffer));
1841 if (ignore_previous_character || (CONSP (defalt) && !NILP (XCDR (defalt))))
1842 is_rear_sticky = false;
1843 else
1845 Lisp_Object rear_non_sticky
1846 = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1848 if (!NILP (CONSP (rear_non_sticky)
1849 ? Fmemq (prop, rear_non_sticky)
1850 : rear_non_sticky))
1851 /* PROP is rear-non-sticky. */
1852 is_rear_sticky = false;
1855 /* Consider following character. */
1856 /* This signals an arg-out-of-range error if pos is outside the
1857 buffer's accessible range. */
1858 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1860 if (EQ (front_sticky, Qt)
1861 || (CONSP (front_sticky)
1862 && !NILP (Fmemq (prop, front_sticky))))
1863 /* PROP is inherited from after. */
1864 is_front_sticky = true;
1866 /* Simple cases, where the properties are consistent. */
1867 if (is_rear_sticky && !is_front_sticky)
1868 return -1;
1869 else if (!is_rear_sticky && is_front_sticky)
1870 return 1;
1871 else if (!is_rear_sticky && !is_front_sticky)
1872 return 0;
1874 /* The stickiness properties are inconsistent, so we have to
1875 disambiguate. Basically, rear-sticky wins, _except_ if the
1876 property that would be inherited has a value of nil, in which case
1877 front-sticky wins. */
1878 if (ignore_previous_character
1879 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1880 return 1;
1881 else
1882 return -1;
1886 /* Copying properties between objects. */
1888 /* Add properties from START to END of SRC, starting at POS in DEST.
1889 SRC and DEST may each refer to strings or buffers.
1890 Optional sixth argument PROP causes only that property to be copied.
1891 Properties are copied to DEST as if by `add-text-properties'.
1892 Return t if any property value actually changed, nil otherwise. */
1894 /* Note this can GC when DEST is a buffer. */
1896 Lisp_Object
1897 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src,
1898 Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1900 INTERVAL i;
1901 Lisp_Object res;
1902 Lisp_Object stuff;
1903 Lisp_Object plist;
1904 ptrdiff_t s, e, e2, p, len;
1905 bool modified = 0;
1906 struct gcpro gcpro1, gcpro2;
1908 i = validate_interval_range (src, &start, &end, soft);
1909 if (!i)
1910 return Qnil;
1912 CHECK_NUMBER_COERCE_MARKER (pos);
1914 Lisp_Object dest_start, dest_end;
1916 e = XINT (pos) + (XINT (end) - XINT (start));
1917 if (MOST_POSITIVE_FIXNUM < e)
1918 args_out_of_range (pos, end);
1919 dest_start = pos;
1920 XSETFASTINT (dest_end, e);
1921 /* Apply this to a copy of pos; it will try to increment its arguments,
1922 which we don't want. */
1923 validate_interval_range (dest, &dest_start, &dest_end, soft);
1926 s = XINT (start);
1927 e = XINT (end);
1928 p = XINT (pos);
1930 stuff = Qnil;
1932 while (s < e)
1934 e2 = i->position + LENGTH (i);
1935 if (e2 > e)
1936 e2 = e;
1937 len = e2 - s;
1939 plist = i->plist;
1940 if (! NILP (prop))
1941 while (! NILP (plist))
1943 if (EQ (Fcar (plist), prop))
1945 plist = list2 (prop, Fcar (Fcdr (plist)));
1946 break;
1948 plist = Fcdr (Fcdr (plist));
1950 if (! NILP (plist))
1951 /* Must defer modifications to the interval tree in case
1952 src and dest refer to the same string or buffer. */
1953 stuff = Fcons (list3 (make_number (p), make_number (p + len), plist),
1954 stuff);
1956 i = next_interval (i);
1957 if (!i)
1958 break;
1960 p += len;
1961 s = i->position;
1964 GCPRO2 (stuff, dest);
1966 while (! NILP (stuff))
1968 res = Fcar (stuff);
1969 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1970 Fcar (Fcdr (Fcdr (res))), dest);
1971 if (! NILP (res))
1972 modified = 1;
1973 stuff = Fcdr (stuff);
1976 UNGCPRO;
1978 return modified ? Qt : Qnil;
1982 /* Return a list representing the text properties of OBJECT between
1983 START and END. if PROP is non-nil, report only on that property.
1984 Each result list element has the form (S E PLIST), where S and E
1985 are positions in OBJECT and PLIST is a property list containing the
1986 text properties of OBJECT between S and E. Value is nil if OBJECT
1987 doesn't contain text properties between START and END. */
1989 Lisp_Object
1990 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1992 struct interval *i;
1993 Lisp_Object result;
1995 result = Qnil;
1997 i = validate_interval_range (object, &start, &end, soft);
1998 if (i)
2000 ptrdiff_t s = XINT (start);
2001 ptrdiff_t e = XINT (end);
2003 while (s < e)
2005 ptrdiff_t interval_end, len;
2006 Lisp_Object plist;
2008 interval_end = i->position + LENGTH (i);
2009 if (interval_end > e)
2010 interval_end = e;
2011 len = interval_end - s;
2013 plist = i->plist;
2015 if (!NILP (prop))
2016 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
2017 if (EQ (XCAR (plist), prop))
2019 plist = list2 (prop, Fcar (XCDR (plist)));
2020 break;
2023 if (!NILP (plist))
2024 result = Fcons (list3 (make_number (s), make_number (s + len),
2025 plist),
2026 result);
2028 i = next_interval (i);
2029 if (!i)
2030 break;
2031 s = i->position;
2035 return result;
2039 /* Add text properties to OBJECT from LIST. LIST is a list of triples
2040 (START END PLIST), where START and END are positions and PLIST is a
2041 property list containing the text properties to add. Adjust START
2042 and END positions by DELTA before adding properties. */
2044 void
2045 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
2047 struct gcpro gcpro1, gcpro2;
2049 GCPRO2 (list, object);
2051 for (; CONSP (list); list = XCDR (list))
2053 Lisp_Object item, start, end, plist;
2055 item = XCAR (list);
2056 start = make_number (XINT (XCAR (item)) + XINT (delta));
2057 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
2058 plist = XCAR (XCDR (XCDR (item)));
2060 Fadd_text_properties (start, end, plist, object);
2063 UNGCPRO;
2068 /* Modify end-points of ranges in LIST destructively, and return the
2069 new list. LIST is a list as returned from text_property_list.
2070 Discard properties that begin at or after NEW_END, and limit
2071 end-points to NEW_END. */
2073 Lisp_Object
2074 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
2076 Lisp_Object prev = Qnil, head = list;
2077 ptrdiff_t max = XINT (new_end);
2079 for (; CONSP (list); prev = list, list = XCDR (list))
2081 Lisp_Object item, beg, end;
2083 item = XCAR (list);
2084 beg = XCAR (item);
2085 end = XCAR (XCDR (item));
2087 if (XINT (beg) >= max)
2089 /* The start-point is past the end of the new string.
2090 Discard this property. */
2091 if (EQ (head, list))
2092 head = XCDR (list);
2093 else
2094 XSETCDR (prev, XCDR (list));
2096 else if (XINT (end) > max)
2097 /* The end-point is past the end of the new string. */
2098 XSETCAR (XCDR (item), new_end);
2101 return head;
2106 /* Call the modification hook functions in LIST, each with START and END. */
2108 static void
2109 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2111 struct gcpro gcpro1;
2112 GCPRO1 (list);
2113 while (!NILP (list))
2115 call2 (Fcar (list), start, end);
2116 list = Fcdr (list);
2118 UNGCPRO;
2121 /* Check for read-only intervals between character positions START ... END,
2122 in BUF, and signal an error if we find one.
2124 Then check for any modification hooks in the range.
2125 Create a list of all these hooks in lexicographic order,
2126 eliminating consecutive extra copies of the same hook. Then call
2127 those hooks in order, with START and END - 1 as arguments. */
2129 void
2130 verify_interval_modification (struct buffer *buf,
2131 ptrdiff_t start, ptrdiff_t end)
2133 INTERVAL intervals = buffer_intervals (buf);
2134 INTERVAL i;
2135 Lisp_Object hooks;
2136 Lisp_Object prev_mod_hooks;
2137 Lisp_Object mod_hooks;
2138 struct gcpro gcpro1;
2140 hooks = Qnil;
2141 prev_mod_hooks = Qnil;
2142 mod_hooks = Qnil;
2144 interval_insert_behind_hooks = Qnil;
2145 interval_insert_in_front_hooks = Qnil;
2147 if (!intervals)
2148 return;
2150 if (start > end)
2152 ptrdiff_t temp = start;
2153 start = end;
2154 end = temp;
2157 /* For an insert operation, check the two chars around the position. */
2158 if (start == end)
2160 INTERVAL prev = NULL;
2161 Lisp_Object before, after;
2163 /* Set I to the interval containing the char after START,
2164 and PREV to the interval containing the char before START.
2165 Either one may be null. They may be equal. */
2166 i = find_interval (intervals, start);
2168 if (start == BUF_BEGV (buf))
2169 prev = 0;
2170 else if (i->position == start)
2171 prev = previous_interval (i);
2172 else if (i->position < start)
2173 prev = i;
2174 if (start == BUF_ZV (buf))
2175 i = 0;
2177 /* If Vinhibit_read_only is set and is not a list, we can
2178 skip the read_only checks. */
2179 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2181 /* If I and PREV differ we need to check for the read-only
2182 property together with its stickiness. If either I or
2183 PREV are 0, this check is all we need.
2184 We have to take special care, since read-only may be
2185 indirectly defined via the category property. */
2186 if (i != prev)
2188 if (i)
2190 after = textget (i->plist, Qread_only);
2192 /* If interval I is read-only and read-only is
2193 front-sticky, inhibit insertion.
2194 Check for read-only as well as category. */
2195 if (! NILP (after)
2196 && NILP (Fmemq (after, Vinhibit_read_only)))
2198 Lisp_Object tem;
2200 tem = textget (i->plist, Qfront_sticky);
2201 if (TMEM (Qread_only, tem)
2202 || (NILP (Fplist_get (i->plist, Qread_only))
2203 && TMEM (Qcategory, tem)))
2204 text_read_only (after);
2208 if (prev)
2210 before = textget (prev->plist, Qread_only);
2212 /* If interval PREV is read-only and read-only isn't
2213 rear-nonsticky, inhibit insertion.
2214 Check for read-only as well as category. */
2215 if (! NILP (before)
2216 && NILP (Fmemq (before, Vinhibit_read_only)))
2218 Lisp_Object tem;
2220 tem = textget (prev->plist, Qrear_nonsticky);
2221 if (! TMEM (Qread_only, tem)
2222 && (! NILP (Fplist_get (prev->plist,Qread_only))
2223 || ! TMEM (Qcategory, tem)))
2224 text_read_only (before);
2228 else if (i)
2230 after = textget (i->plist, Qread_only);
2232 /* If interval I is read-only and read-only is
2233 front-sticky, inhibit insertion.
2234 Check for read-only as well as category. */
2235 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2237 Lisp_Object tem;
2239 tem = textget (i->plist, Qfront_sticky);
2240 if (TMEM (Qread_only, tem)
2241 || (NILP (Fplist_get (i->plist, Qread_only))
2242 && TMEM (Qcategory, tem)))
2243 text_read_only (after);
2245 tem = textget (prev->plist, Qrear_nonsticky);
2246 if (! TMEM (Qread_only, tem)
2247 && (! NILP (Fplist_get (prev->plist, Qread_only))
2248 || ! TMEM (Qcategory, tem)))
2249 text_read_only (after);
2254 /* Run both insert hooks (just once if they're the same). */
2255 if (prev)
2256 interval_insert_behind_hooks
2257 = textget (prev->plist, Qinsert_behind_hooks);
2258 if (i)
2259 interval_insert_in_front_hooks
2260 = textget (i->plist, Qinsert_in_front_hooks);
2262 else
2264 /* Loop over intervals on or next to START...END,
2265 collecting their hooks. */
2267 i = find_interval (intervals, start);
2270 if (! INTERVAL_WRITABLE_P (i))
2271 text_read_only (textget (i->plist, Qread_only));
2273 if (!inhibit_modification_hooks)
2275 mod_hooks = textget (i->plist, Qmodification_hooks);
2276 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2278 hooks = Fcons (mod_hooks, hooks);
2279 prev_mod_hooks = mod_hooks;
2283 if (i->position + LENGTH (i) < end
2284 && (!NILP (BVAR (current_buffer, read_only))
2285 && NILP (Vinhibit_read_only)))
2286 xsignal1 (Qbuffer_read_only, Fcurrent_buffer ());
2288 i = next_interval (i);
2290 /* Keep going thru the interval containing the char before END. */
2291 while (i && i->position < end);
2293 if (!inhibit_modification_hooks)
2295 GCPRO1 (hooks);
2296 hooks = Fnreverse (hooks);
2297 while (! EQ (hooks, Qnil))
2299 call_mod_hooks (Fcar (hooks), make_number (start),
2300 make_number (end));
2301 hooks = Fcdr (hooks);
2303 UNGCPRO;
2308 /* Run the interval hooks for an insertion on character range START ... END.
2309 verify_interval_modification chose which hooks to run;
2310 this function is called after the insertion happens
2311 so it can indicate the range of inserted text. */
2313 void
2314 report_interval_modification (Lisp_Object start, Lisp_Object end)
2316 if (! NILP (interval_insert_behind_hooks))
2317 call_mod_hooks (interval_insert_behind_hooks, start, end);
2318 if (! NILP (interval_insert_in_front_hooks)
2319 && ! EQ (interval_insert_in_front_hooks,
2320 interval_insert_behind_hooks))
2321 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2324 void
2325 syms_of_textprop (void)
2327 DEFVAR_LISP ("default-text-properties", Vdefault_text_properties,
2328 doc: /* Property-list used as default values.
2329 The value of a property in this list is seen as the value for every
2330 character that does not have its own value for that property. */);
2331 Vdefault_text_properties = Qnil;
2333 DEFVAR_LISP ("char-property-alias-alist", Vchar_property_alias_alist,
2334 doc: /* Alist of alternative properties for properties without a value.
2335 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2336 If a piece of text has no direct value for a particular property, then
2337 this alist is consulted. If that property appears in the alist, then
2338 the first non-nil value from the associated alternative properties is
2339 returned. */);
2340 Vchar_property_alias_alist = Qnil;
2342 DEFVAR_LISP ("inhibit-point-motion-hooks", Vinhibit_point_motion_hooks,
2343 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2344 This also inhibits the use of the `intangible' text property. */);
2345 Vinhibit_point_motion_hooks = Qnil;
2347 DEFVAR_LISP ("text-property-default-nonsticky",
2348 Vtext_property_default_nonsticky,
2349 doc: /* Alist of properties vs the corresponding non-stickiness.
2350 Each element has the form (PROPERTY . NONSTICKINESS).
2352 If a character in a buffer has PROPERTY, new text inserted adjacent to
2353 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2354 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2355 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2356 /* Text properties `syntax-table'and `display' should be nonsticky
2357 by default. */
2358 Vtext_property_default_nonsticky
2359 = list2 (Fcons (intern_c_string ("syntax-table"), Qt),
2360 Fcons (intern_c_string ("display"), Qt));
2362 staticpro (&interval_insert_behind_hooks);
2363 staticpro (&interval_insert_in_front_hooks);
2364 interval_insert_behind_hooks = Qnil;
2365 interval_insert_in_front_hooks = Qnil;
2368 /* Common attributes one might give text. */
2370 DEFSYM (Qforeground, "foreground");
2371 DEFSYM (Qbackground, "background");
2372 DEFSYM (Qfont, "font");
2373 DEFSYM (Qface, "face");
2374 DEFSYM (Qstipple, "stipple");
2375 DEFSYM (Qunderline, "underline");
2376 DEFSYM (Qread_only, "read-only");
2377 DEFSYM (Qinvisible, "invisible");
2378 DEFSYM (Qintangible, "intangible");
2379 DEFSYM (Qcategory, "category");
2380 DEFSYM (Qlocal_map, "local-map");
2381 DEFSYM (Qfront_sticky, "front-sticky");
2382 DEFSYM (Qrear_nonsticky, "rear-nonsticky");
2383 DEFSYM (Qmouse_face, "mouse-face");
2384 DEFSYM (Qminibuffer_prompt, "minibuffer-prompt");
2386 /* Properties that text might use to specify certain actions. */
2388 DEFSYM (Qmouse_left, "mouse-left");
2389 DEFSYM (Qmouse_entered, "mouse-entered");
2390 DEFSYM (Qpoint_left, "point-left");
2391 DEFSYM (Qpoint_entered, "point-entered");
2393 defsubr (&Stext_properties_at);
2394 defsubr (&Sget_text_property);
2395 defsubr (&Sget_char_property);
2396 defsubr (&Sget_char_property_and_overlay);
2397 defsubr (&Snext_char_property_change);
2398 defsubr (&Sprevious_char_property_change);
2399 defsubr (&Snext_single_char_property_change);
2400 defsubr (&Sprevious_single_char_property_change);
2401 defsubr (&Snext_property_change);
2402 defsubr (&Snext_single_property_change);
2403 defsubr (&Sprevious_property_change);
2404 defsubr (&Sprevious_single_property_change);
2405 defsubr (&Sadd_text_properties);
2406 defsubr (&Sput_text_property);
2407 defsubr (&Sset_text_properties);
2408 defsubr (&Sadd_face_text_property);
2409 defsubr (&Sremove_text_properties);
2410 defsubr (&Sremove_list_of_text_properties);
2411 defsubr (&Stext_property_any);
2412 defsubr (&Stext_property_not_all);